✨ feat(vscode): 代码阅读预览顶部加块筛选栏

语言插件按和语言无关的 id 报块属性(通用的由宿主从 facts 推),翻译插件起名,webview 只按 id 筛;
筛选整个工作区共用、记在 workspaceState;JavaScript 不再显示「抽象:否」「仅类型:否」
This commit is contained in:
cruldra committed 2026-09-25 21:31:19 +08:00
1 parent 4eaf2ae11d
commit 1c7e31c51a
29 files changed
+1364 -319

No files matched your search

+23 -25
View File
@@ -1,36 +1,23 @@
import type { ReactElement } from 'react'
import type { BlockKind, BlockView } from './types'
import { ChevronDown, ChevronRight, Eye, Pencil } from 'lucide-react'
import { ChevronDown, ChevronRight, Eye, EyeOff, Pencil } from 'lucide-react'
import { Fragment } from 'react'
import { cn } from '@/lib/utils'
import { CodeEditor } from './CodeEditor'
import { PropertyGrid } from './PropertyGrid'
import { TextPreview } from './TextPreview'
const KIND_LABEL: Record<BlockKind, string> = {
import: '导入',
function: '函数',
class: '类',
type: '类型',
field: '字段',
const: '常量',
variable: '变量',
if: '条件',
else: '否则',
loop: '循环',
try: '尝试',
catch: '捕获',
finally: '最终',
return: '返回',
statement: '语句',
comment: '注释',
}
export interface BlockTreeState {
version: number
editing: ReadonlySet<string>
/** 眼下折叠着的块 id、属性行 id 和子行 id(propertyRowId、propertyItemId):用户的选择叠在默认上,见 collapsedIds */
collapsed: ReadonlySet<string>
/** 按筛选不画的块(连同子孙),见 applyFilter */
hidden: ReadonlySet<string>
/** 本来被筛掉、跳到定义时临时显示的块 */
temporary: ReadonlySet<string>
/** 种类标签上写什么:翻译插件给种类起的名字,没给就是 id */
kindName: (kind: BlockKind) => string
/** 刚点了「编辑」、要把光标放进去的块 */
focusId: string | null
onFocused: () => void
@@ -38,8 +25,13 @@ export interface BlockTreeState {
onToggleFold: (id: string) => void
}
/** 块下面有没有画出来的子块:子块都被筛掉时不留空的一层、不给折叠箭头。 */
function hasShownChildren(block: BlockView, tree: BlockTreeState): boolean {
return block.children.some(child => !tree.hidden.has(child.id))
}
/**
* 一层代码块:左边一条竖线标出这一层,每块的子块紧跟在它下面、再往右缩进一层。
* 一层代码块:左边一条竖线标出这一层,每块的子块紧跟在它下面、再往右缩进一层。被筛掉的块不画,不留空位,竖线跟着变短。
* kind 是这一层所属块的种类(顶层没有),悬停那个块时竖线换成它的颜色。
*/
export function BlockLevel({ blocks, kind, tree }: { blocks: BlockView[], kind?: BlockKind, tree: BlockTreeState }): ReactElement {
@@ -49,10 +41,10 @@ export function BlockLevel({ blocks, kind, tree }: { blocks: BlockView[], kind?:
<div className="cr-guide w-px" />
</div>
<div className="flex min-w-0 flex-1 flex-col gap-2">
{blocks.map(block => (
{blocks.filter(block => !tree.hidden.has(block.id)).map(block => (
<Fragment key={block.id}>
<BlockCard block={block} tree={tree} />
{block.children.length > 0 && !tree.collapsed.has(block.id) && (
{hasShownChildren(block, tree) && !tree.collapsed.has(block.id) && (
<BlockLevel blocks={block.children} kind={block.kind} tree={tree} />
)}
</Fragment>
@@ -73,7 +65,7 @@ function BlockPreview({ block, tree }: { block: BlockView, tree: BlockTreeState
function BlockCard({ block, tree }: { block: BlockView, tree: BlockTreeState }): ReactElement {
const editing = tree.editing.has(block.id)
const collapsed = tree.collapsed.has(block.id)
const hasChildren = block.children.length > 0
const hasChildren = hasShownChildren(block, tree)
const Chevron = collapsed ? ChevronRight : ChevronDown
return (
<div
@@ -97,7 +89,7 @@ function BlockCard({ block, tree }: { block: BlockView, tree: BlockTreeState }):
)
: <span className="w-3.5 shrink-0" />}
<span className="cr-badge flex h-5 w-11 shrink-0 items-center justify-center rounded text-[11px] leading-[14px] font-medium">
{KIND_LABEL[block.kind]}
{tree.kindName(block.kind)}
</span>
<div className="min-w-0 flex-1">
{editing
@@ -112,6 +104,12 @@ function BlockCard({ block, tree }: { block: BlockView, tree: BlockTreeState }):
)
: <BlockPreview block={block} tree={tree} />}
</div>
{tree.temporary.has(block.id) && (
<span className="cr-temporary flex h-5 shrink-0 items-center gap-1 rounded px-1.5 text-[11px] whitespace-nowrap">
<EyeOff size={12} strokeWidth={1.8} aria-hidden />
已被筛选隐藏,临时显示
</span>
)}
<button
type="button"
className={cn(