✨ feat(vscode): 代码阅读页面画链式调用卡片:竖轨道、小链、收起的闭包和合成的表
链的各步排在值下面的竖轨道上,方法一列对齐、轨道线是函数色淡到 35%;实参是链的缩进成空心点小链,注释挂在步骤下;收起的闭包是能点的小框,点开那一步才画挂着的子块(折叠 id 记在属性行下面);跳到定义、查找、高度估算、只看改动都算上链里的字和步骤下的子块
This commit is contained in:
1 parent
ee595f3da9
commit
1956879aa1
13 files changed
+531
-47
No files matched your search
@@ -3,7 +3,7 @@ import type { SourceText } from './blockSource'
|
||||
import type { RewriteTool } from './BlockTools'
|
||||
import type { DiffTree, FoldedRuns, PlacedRemoved } from './diffView'
|
||||
import type { Place, Placement } from './placement'
|
||||
import type { GridSlots } from './PropertyGrid'
|
||||
import type { ChainSlots, GridSlots } from './PropertyGrid'
|
||||
import type { ChatState, ChatView, QaState } from './qa'
|
||||
import type { PendingImage } from './qaImages'
|
||||
import type { ReadableStore } from './store'
|
||||
@@ -22,7 +22,7 @@ import { chatOf, EMPTY_CHAT, sameChat, unfinished } from './qa'
|
||||
import { BlockIdContext } from './SegmentText'
|
||||
import { shallowEqual, staticStore, useStore } from './store'
|
||||
import { TextPreview } from './TextPreview'
|
||||
import { foldsByCard, listId, tableId, textKey } from './viewState'
|
||||
import { chainStepId, foldsByCard, listId, tableId, textKey } from './viewState'
|
||||
|
||||
/** 块树里改代码的那部分状态:哪些块在编辑。页面上的块树才有;定义卡片浮窗里的块树没有,卡片只读,不画右上角的按钮 */
|
||||
export interface EditState {
|
||||
@@ -150,10 +150,15 @@ const NO_CHAT: ReadableStore<ChatState> = staticStore(EMPTY_CHAT)
|
||||
|
||||
/**
|
||||
* 块下面有没有画出来的子块(删掉的子块也算):子块都被筛掉、被盖住时不留空的一层、不给折叠箭头。
|
||||
* 挂在卡片子行上的子块(BlockSlot)画在卡片属性表里,不算。
|
||||
* 挂在卡片子行上的子块(BlockSlot)、挂在链式调用某一步下面的子块(ChainSlot)画在卡片里,不算。
|
||||
*/
|
||||
function hasShownChildren(block: BlockView, state: BlockTreeState): boolean {
|
||||
return block.children.some(child => !child.slot && shown(child, state)) || (!state.covered.has(block.id) && state.diff?.removed.has(block.id) === true)
|
||||
return block.children.some(child => !inCard(child) && shown(child, state)) || (!state.covered.has(block.id) && state.diff?.removed.has(block.id) === true)
|
||||
}
|
||||
|
||||
/** 子块画在父卡片里(属性表子行上的 BlockSlot、链式调用某一步下面的 ChainSlot),不排在父卡片后面那一层。 */
|
||||
function inCard(block: BlockView): boolean {
|
||||
return block.slot !== undefined || block.step !== undefined
|
||||
}
|
||||
|
||||
const CHANGE_NAMES = { add: '新增', mod: '修改', del: '删除' } as const
|
||||
@@ -372,12 +377,12 @@ function Item({ item, height, children }: { item: boolean, height: number, child
|
||||
* 一层代码块:左边一条竖线标出这一层,每块的子块紧跟在它下面、再往右缩进一层。被筛掉、被盖住的块不画,不留空位,竖线跟着变短。
|
||||
* owner 是这一层所属的块(顶层没有,记在 data-owner 上,钉住卡片时按它算卡片连同子块管到哪、套着几层),悬停它时竖线换成它的种类色。
|
||||
* 标差异时每张卡片左边外侧一条改动色条,删掉的块收成一行放回原来的位置(见 RemovedCard);「只看改动」时连着没变的块折成一条(UnchangedStrip)。
|
||||
* slotted:这一层是画在卡片属性表某一行下面的子块(BlockSlot),blocks 只有那几块。items:这一层每块套一层 .cr-item(只有页面上的顶层)。
|
||||
* slotted:这一层是画在卡片里的子块(属性表某一行下面的 BlockSlot、链式调用某一步下面的 ChainSlot),blocks 只有那几块。items:这一层每块套一层 .cr-item(只有页面上的顶层)。
|
||||
*/
|
||||
export function BlockLevel({ blocks, owner, slotted = false, items = false }: { blocks: BlockView[], owner?: CardView, slotted?: boolean, items?: boolean }): ReactElement {
|
||||
const tree = useBlockTree()
|
||||
// 挂在卡片子行上的子块画在卡片属性表里(slotted 的那一层),这一层不画;删掉的块都放回卡片后面这一层
|
||||
const listed = useMemo(() => slotted ? blocks : blocks.filter(block => !block.slot), [blocks, slotted])
|
||||
// 挂在卡片子行上、链式调用某一步下面的子块画在卡片里(slotted 的那一层),这一层不画;删掉的块都放回卡片后面这一层
|
||||
const listed = useMemo(() => slotted ? blocks : blocks.filter(block => !inCard(block)), [blocks, slotted])
|
||||
const { removed, diff } = useStore(tree.state, state => ({
|
||||
removed: slotted ? NO_REMOVED : state.diff?.removed.get(owner?.id ?? '') ?? NO_REMOVED,
|
||||
diff: state.diff !== undefined,
|
||||
@@ -418,12 +423,15 @@ export function BlockLevel({ blocks, owner, slotted = false, items = false }: {
|
||||
const BlockPreview = memo(({ block, children = NO_CHILDREN, folds, marks }: { block: CardView, children?: BlockView[], folds: ReadonlySet<string>, marks?: BlockMarks }): ReactElement => {
|
||||
const tree = useBlockTree()
|
||||
const slots = useMemo(() => cardSlots(block, children, folds, tree), [block, children, folds, tree])
|
||||
const chain = useMemo(() => chainSlots(block, children, folds, tree), [block, children, folds, tree])
|
||||
if ('properties' in block)
|
||||
return <PropertyGrid blockId={block.id} rows={block.properties} collapsed={folds} onToggleFold={tree.onToggleFold} marks={marks?.rows} slots={slots} />
|
||||
return <PropertyGrid blockId={block.id} rows={block.properties} collapsed={folds} onToggleFold={tree.onToggleFold} marks={marks?.rows} slots={slots} chain={chain} />
|
||||
const folding = { collapsed: folds, onToggleFold: tree.onToggleFold }
|
||||
// 整句是链的块译文只剩链外面的词,什么都没剩时不画这一行
|
||||
const empty = Array.isArray(block.translation) && block.translation.length === 0
|
||||
return (
|
||||
<>
|
||||
{marks?.translation ? <DiffText diff={marks.translation} find={textKey(block.id, 'text')} /> : <TextPreview text={block.translation} find={textKey(block.id, 'text')} />}
|
||||
{marks?.translation ? <DiffText diff={marks.translation} find={textKey(block.id, 'text')} /> : !empty && <TextPreview text={block.translation} find={textKey(block.id, 'text')} />}
|
||||
{block.items && (
|
||||
<div className="mt-[3px]">
|
||||
<PropertyItems parentId={listId(block.id)} items={block.items} folding={folding} marks={marks?.items} />
|
||||
@@ -436,8 +444,8 @@ const BlockPreview = memo(({ block, children = NO_CHILDREN, folds, marks }: { bl
|
||||
)}
|
||||
{marks?.doc ? <DiffText diff={marks.doc} className="mt-0.5" find={textKey(block.id, 'doc')} /> : block.doc && <TextPreview text={block.doc} className="mt-0.5" find={textKey(block.id, 'doc')} />}
|
||||
{block.rows && (
|
||||
<div className="mt-[3px]">
|
||||
<PropertyGrid blockId={block.id} rows={block.rows} collapsed={folds} onToggleFold={tree.onToggleFold} marks={marks?.rows} />
|
||||
<div className={empty ? undefined : 'mt-[3px]'}>
|
||||
<PropertyGrid blockId={block.id} rows={block.rows} collapsed={folds} onToggleFold={tree.onToggleFold} marks={marks?.rows} chain={chain} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
@@ -467,6 +475,22 @@ function cardSlots(block: CardView, children: readonly BlockView[], folds: Reado
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 挂在链式调用某一步下面的子块(ChainSlot)怎么画:一步下面的几块画成一层块(同块树);那一步的折叠 id(chainStepId)没折着就画,
|
||||
* 点那一步收起来的闭包时切换它。卡片没有这样的子块时没有。
|
||||
*/
|
||||
function chainSlots(block: CardView, children: readonly BlockView[], folds: ReadonlySet<string>, tree: BlockTreeHandle): ChainSlots | undefined {
|
||||
const blocks = children.filter(child => child.step)
|
||||
if (blocks.length === 0)
|
||||
return undefined
|
||||
return {
|
||||
blocks,
|
||||
render: list => <BlockLevel blocks={list} owner={block} slotted />,
|
||||
open: slot => !folds.has(chainStepId(block.id, slot)),
|
||||
onToggle: slot => tree.onToggleFold(chainStepId(block.id, slot)),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 一块的卡片:折叠箭头、种类图标、预览内容或源码、右上角的一排图标按钮(BlockTools,只读的块树没有)。
|
||||
* 卡片上记着块记号(data-anchor),说明里不带路径的引用靠它认自己写在哪块(见 commentLinkOf)。
|
||||
|
||||
Reference in new issue
Block a user