✨ feat(vscode): 代码阅读函数块默认折叠,函数说明进属性表「说明」行

This commit is contained in:
cruldra committed 2026-09-25 13:09:53 +08:00
1 parent 62f48fe67c
commit 9b32d4b5a8
17 files changed
+489 -100

No files matched your search

+45 -5
View File
@@ -1,17 +1,20 @@
import type { ScrollAnchor } from './scrollPosition'
import type { BlockKind, BlockView } from './types'
/**
* 标签页切走时 VS Code 会销毁 webview、切回来重新加载,靠它恢复:哪些块停在「编辑」、
* 哪些块和属性行折叠了(块 id 是它在树里的位置,文档刷新后照旧对得上;属性行 id 见 propertyRowId),以及滚到了哪。
* 用户把哪些块和属性行折叠 / 展开了(块 id 是它在树里的位置,文档刷新后照旧对得上;属性行 id 见 propertyRowId),以及滚到了哪。
*/
export interface ViewState {
editing: string[]
collapsed: string[]
/** 用户自己点过折叠箭头的块和属性行:id → 折叠着没有。没点过的按默认,见 collapsedIds */
folds: Record<string, boolean>
scroll?: ScrollAnchor
}
export interface ViewStateStorage {
getState: () => ViewState | undefined
/** 存下来的东西可能是旧版本的形状,读的时候逐项核对 */
getState: () => unknown
setState: (state: ViewState) => void
}
@@ -23,7 +26,7 @@ export interface ViewStateStore {
/** 一个 webview 只有一份状态:块的状态和滚动位置由不同地方各自更新,存的时候合在一起。 */
export function createViewStateStore(storage: ViewStateStorage): ViewStateStore {
let current: ViewState = storage.getState() ?? { editing: [], collapsed: [] }
let current = readViewState(storage.getState())
return {
get: () => current,
update: (patch) => {
@@ -33,8 +36,45 @@ export function createViewStateStore(storage: ViewStateStorage): ViewStateStore
}
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
function isScrollAnchor(value: unknown): value is ScrollAnchor {
return isRecord(value) && [value.line, value.offset, value.scrollTop].every(field => typeof field === 'number')
}
/** 存下来的状态逐项核对,形状不对的项(包括旧版本存的)当没存过。 */
function readViewState(saved: unknown): ViewState {
const state = isRecord(saved) ? saved : {}
const editing = Array.isArray(state.editing) ? state.editing.filter((id): id is string => typeof id === 'string') : []
const folds = isRecord(state.folds)
? Object.fromEntries(Object.entries(state.folds).filter((entry): entry is [string, boolean] => typeof entry[1] === 'boolean'))
: {}
return isScrollAnchor(state.scroll) ? { editing, folds, scroll: state.scroll } : { editing, folds }
}
/** 块默认折叠吗:函数块收起主体(属性表照常显示),其余块展开。属性行默认都展开。 */
export function foldedByDefault(kind: BlockKind): boolean {
return kind === 'function'
}
/** 眼下折叠着的块和属性行:用户点过的按他点的,没点过的按 foldedByDefault。 */
export function collapsedIds(blocks: readonly BlockView[], folds: Readonly<Record<string, boolean>>): Set<string> {
const collapsed = new Set(Object.keys(folds).filter(id => folds[id]))
const visit = (list: readonly BlockView[]): void => {
for (const block of list) {
if (folds[block.id] === undefined && foldedByDefault(block.kind))
collapsed.add(block.id)
visit(block.children)
}
}
visit(blocks)
return collapsed
}
/**
* 属性表里带子行的那一行(输入参数、导入项)的折叠 id,和块 id 放在同一个 collapsed 里:
* 属性表里带子行的那一行(输入参数、导入项)的折叠 id,和块 id 放在同一份 folds 里:
* `<块 id>/p<行序号>`,例如 '6.1.0/p3'。块 id 只由数字和点组成,带 '/' 的不会和任何块 id 撞上。
*/
export function propertyRowId(blockId: string, rowIndex: number): string {