✨ feat(vscode): React 钩子卡片按变量讲,属性表加操作列、调用树能查所有引用
- 钩子调用讲成状态、引用、记忆值、记忆函数、上下文值、过渡、副作用、钩子返回值、钩子调用几种卡片,行照签名表 reactHooks.ts 排,去掉 react.hook - 解构接住时名称一行写出解构,展开是「第几项 | 名称 | 说明」表,每项一个调用树操作;单个名字的按钮在名称一行最右边 - 属性表的行、子行可以带操作(RowAction),表格多一列「操作」,宿主按块记号加序号起键 - 调用树服务按键数次数,新增查所有引用(references):按所在函数分组,去掉定义处和依赖数组里的 - 钩子收的函数参数都收成小框,函数体是卡片子块,直接返回的表达式由 functionBody 排成「返回 …」卡片;小框和卡片折叠箭头同一个开关 - 规则卡片可指定语言种类图标(languageKind)、种类图标悬停字和颜色(KindBadge.tooltip / iconColor)
This commit is contained in:
1 parent
a139341132
commit
86beaac371
38 files changed
+1971
-813
No files matched your search
@@ -226,8 +226,9 @@ export interface StickyScrollSettings {
|
||||
* repoLinkBuiltin:点的不带路径的引用是语言自带、又问不到定义的名字,没打开,页面当普通文字;symbol、block、file 同 openRepoLink
|
||||
* repoLinkOpened:这个仓库链接打开了。路径在;带了 symbol 时这个符号也在;没有 path 是不带路径的引用找到了,block、file 同 openRepoLink
|
||||
* repoImagesChecked:id 那次 checkRepoImages 的回答。src 是预览里能直接用的图片地址;没有 src 是文件不在
|
||||
* callTreeCounts:version 这份文档里,每个能看调用树的块(BlockView.callTree)落在几棵调用树里(只包括大于 0 的)。不是页面上这个版本的不理
|
||||
* callTree:anchor 那块的调用树。trees 空着、又没有 contractMiss 是没有调用它的地方;接口卡片在契约文件里没对上时带 contractMiss。
|
||||
* callTreeCounts:version 这份文档里各个调用树的次数(只包括大于 0 的),按调用树的键:能看调用树的块(BlockView.callTree)是块的记号,
|
||||
* 落在几棵调用树里;卡片属性表里的操作([看调用树的操作](vscode/src/codeReader/contract.ts#CallTreeAction))是它的键,按它的查法算次数。不是页面上这个版本的不理
|
||||
* callTree:键是 key 的那个调用树(同 callTreeCounts)。trees 空着、又没有 contractMiss 是没有调用它的地方;接口卡片在契约文件里没对上时带 contractMiss。
|
||||
* 不是页面上这个版本的不理
|
||||
*/
|
||||
export type ExtensionToWebview
|
||||
@@ -259,7 +260,7 @@ export type ExtensionToWebview
|
||||
| { type: 'repoImagesChecked', id: number, images: { path: string, src?: string }[] }
|
||||
| { type: 'focusPathAsk' }
|
||||
| { type: 'callTreeCounts', version: number, counts: Record<string, number> }
|
||||
| { type: 'callTree', version: number, anchor: string, name: string, where: string, trees: CallTree[], contractMiss?: ContractMiss }
|
||||
| { type: 'callTree', version: number, key: string, name: string, where: string, trees: CallTree[], contractMiss?: ContractMiss }
|
||||
|
||||
/**
|
||||
* ready:页面准备好了,可以收消息;openedFrom 是页面记着的这个标签页是从哪儿打开的,新开的页面没记过就没有
|
||||
@@ -292,7 +293,7 @@ export type ExtensionToWebview
|
||||
* block 是它写在哪块的注释里(见 SymbolReference),从那块的声明起一层层往外找那份文件定义的,再是导入的(见 symbolLinks.ts)
|
||||
* checkRepoLinks:预览里这批仓库路径问哪些文件或目录没有,这批不带路径的引用(references)问哪些找不到。id 标出是哪一次问
|
||||
* checkRepoImages:预览里这批仓库图片,问哪些文件在,在的要预览里能直接用的地址。id 标出是哪一次问
|
||||
* openCallTree:打开 anchor 那块的调用树。version 是这块所在的文档版本,对不上就不查
|
||||
* openCallTree:打开键是 key 的调用树(块的记号,或卡片上一个操作的键,同 callTreeCounts)。version 是它所在的文档版本,对不上就不查
|
||||
* goToCallNode:点了调用树上的一个节点,打开 uri 那个文件,跳到 line 行 character 列(都从 0 起,见 CallTreeNode.jump)
|
||||
*/
|
||||
export type WebviewToExtension
|
||||
@@ -320,5 +321,5 @@ export type WebviewToExtension
|
||||
| { type: 'openRepoLink', path?: string, symbol?: string, block?: string, file?: string, lines?: LineRange }
|
||||
| { type: 'checkRepoLinks', id: number, paths: string[], references: SymbolReference[] }
|
||||
| { type: 'checkRepoImages', id: number, paths: string[] }
|
||||
| { type: 'openCallTree', version: number, anchor: string }
|
||||
| { type: 'openCallTree', version: number, key: string }
|
||||
| { type: 'goToCallNode', uri: string, line: number, character: number }
|
||||
@@ -93,14 +93,29 @@ describe('链式调用卡片发给页面', () => {
|
||||
})
|
||||
|
||||
describe('react 钩子卡片发给页面', () => {
|
||||
it('带着按来源分的淡底;调用树查的名字(返回的 setN)同别的下标从块第一行的行首算', async () => {
|
||||
it('钩子返回值带着按来源分的淡底;属性表里各项的操作查的名字、不算的范围同别的下标从块第一行的行首算,键原样带着', async () => {
|
||||
const ts = createTypeScriptAdapter(WASM_DIR)
|
||||
const source = ['import { useState } from "react";', 'export function Box() {', ' const [n, setN] = useState(0);', ' return <i>{n}</i>;', '}'].join('\n')
|
||||
const source = [
|
||||
'import { useEffect, useState } from "react";',
|
||||
'export function Box() {',
|
||||
' const [n, setN] = useState(0);',
|
||||
' const id = useId();',
|
||||
' useEffect(() => {}, [n]);',
|
||||
' return <i>{n}{id}</i>;',
|
||||
'}',
|
||||
].join('\n')
|
||||
const views = await withBlocks(ts, 'typescriptreact', source, blocks =>
|
||||
toBlockViews(blocks, { source, language: ts, markers: [] }, typescriptZhTranslator, { languageId: 'typescriptreact', rules: [reactRule], report: () => {} }))
|
||||
const hook = pageBlocks(views, source)[1].children[0]
|
||||
expect(hook.kind).toBe('react.hook')
|
||||
expect(hook.tint).toEqual({ dark: '#61dafb', light: '#0a7ea4' })
|
||||
expect(hook.callTree).toEqual({ name: { startIndex: ' const [n, '.length, endIndex: ' const [n, setN'.length } })
|
||||
const [state, id] = pageBlocks(views, source)[1].children
|
||||
expect(state.kind).toBe('react.state')
|
||||
expect(state.callTree).toBeUndefined()
|
||||
const base = source.indexOf(' const [n')
|
||||
const deps = { startIndex: source.indexOf('[n]);') - base, endIndex: source.indexOf(');\n return') - base }
|
||||
expect('properties' in state && state.properties[0].items?.map(item => item.actions)).toEqual([
|
||||
[{ kind: 'callTree', name: { startIndex: ' const ['.length, endIndex: ' const [n'.length }, mode: 'references', label: '谁用了这个状态', except: [deps], key: `${state.anchor}|0` }],
|
||||
[{ kind: 'callTree', name: { startIndex: ' const [n, '.length, endIndex: ' const [n, setN'.length }, mode: 'references', label: '谁改了这个状态', except: [deps], key: `${state.anchor}|1` }],
|
||||
])
|
||||
expect(id.kind).toBe('react.hookValue')
|
||||
expect(id.tint).toEqual({ dark: '#61dafb', light: '#0a7ea4' })
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { BlockView } from '../blockView'
|
||||
import type { ChainStep, Inline, PreviewTable, PreviewText, PropertyItem, PropertyRow, Segment, SourceRange } from '../contract'
|
||||
import type { ChainStep, Inline, PreviewTable, PreviewText, PropertyItem, PropertyRow, RowAction, Segment, SourceRange } from '../contract'
|
||||
import type { BlockMarks, ItemDiff, ItemsDiff, RowDiff, RowsDiff, TextDiff } from '../diff/viewDiff'
|
||||
import type { BlockSpan, PageBlock, PageCard } from './messages'
|
||||
import { createHash } from 'node:crypto'
|
||||
@@ -50,6 +50,11 @@ function shiftText(text: PreviewText, base: number): PreviewText {
|
||||
return 'markdown' in text ? text : shiftInline(text, base)
|
||||
}
|
||||
|
||||
/** 一个操作查的名字、不算的范围按块的开头算;键原样留着(页面按它对次数)。 */
|
||||
function shiftAction(action: RowAction, base: number): RowAction {
|
||||
return { ...action, name: shiftRange(action.name, base), ...(action.except && { except: action.except.map(range => shiftRange(range, base)) }) }
|
||||
}
|
||||
|
||||
function shiftItem(item: PropertyItem, base: number): PropertyItem {
|
||||
return {
|
||||
...item,
|
||||
@@ -57,6 +62,7 @@ function shiftItem(item: PropertyItem, base: number): PropertyItem {
|
||||
...(item.defines && { defines: shiftRange(item.defines, base) }),
|
||||
...(item.items && { items: item.items.map(inner => shiftItem(inner, base)) }),
|
||||
...(item.table && { table: shiftTable(item.table, base) }),
|
||||
...(item.actions && { actions: item.actions.map(action => shiftAction(action, base)) }),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +77,7 @@ function shiftRow(row: PropertyRow, base: number): PropertyRow {
|
||||
...(row.defines && { defines: shiftRange(row.defines, base) }),
|
||||
...(row.items && { items: row.items.map(item => shiftItem(item, base)) }),
|
||||
...(row.chain && { chain: row.chain.map(step => shiftStep(step, base)) }),
|
||||
...(row.actions && { actions: row.actions.map(action => shiftAction(action, base)) }),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +107,7 @@ export function pageCard(block: BlockView, base: number): PageCard {
|
||||
...(block.tint && { tint: block.tint }),
|
||||
...(block.slot && { slot: block.slot }),
|
||||
...(block.step && { step: block.step }),
|
||||
...(block.callTree && { callTree: block.callTree.name ? { ...block.callTree, name: shiftRange(block.callTree.name, base) } : block.callTree }),
|
||||
...(block.callTree && { callTree: block.callTree }),
|
||||
}
|
||||
if ('properties' in block)
|
||||
return { ...card, properties: block.properties.map(row => shiftRow(row, base)) }
|
||||
|
||||
@@ -26,7 +26,7 @@ import { commands, EndOfLine, Position, Range, Uri, window, workspace, Workspace
|
||||
import { logger } from '../../logging/logger'
|
||||
import { webviewHtml, webviewUiRoot } from '../../webview/html'
|
||||
import { DocumentCallees } from '../calleeKinds'
|
||||
import { loadBlockCalls } from '../callTree/query'
|
||||
import { loadTargetCalls } from '../callTree/query'
|
||||
import { CallTreeSession } from '../callTree/session'
|
||||
import { commentFollowUp, commentJob, commentRewritePrompt, findBlock, writeCommentBody } from '../commentRewrite'
|
||||
import { DefinitionKinds } from '../definitionKinds'
|
||||
@@ -271,7 +271,7 @@ class Preview {
|
||||
private sent: SentDocument | undefined
|
||||
/** 发文档后在后台问语言扩展的请求(中文名、库规则查的定义、调用的种类)排队发:在途的有上限,离视口近的先发,文档改了排着的作废 */
|
||||
private readonly requests = new LanguageRequests(() => this.document.version, () => this.visibleLine)
|
||||
/** 能看调用树的块(函数、类、接口卡片)落在几棵调用树里。文档发出去之后再数,不挡住第一次预览,离视口近的先数;同一版文档数过的页面重新加载后接着用 */
|
||||
/** 各个调用树的次数(能看调用树的块落在几棵调用树里,卡片上各行的操作见 [CallTreeSession])。文档发出去之后再数,不挡住第一次预览,离视口近的先数;同一版文档数过的页面重新加载后接着用 */
|
||||
private readonly callTrees = new CallTreeSession(
|
||||
message => this.post(message),
|
||||
() => {
|
||||
@@ -280,7 +280,7 @@ class Preview {
|
||||
return {
|
||||
version: this.sent.version,
|
||||
blocks: this.sent.blocks,
|
||||
load: (block, cancelled) => loadBlockCalls(this.document, block, this.owner.indexed, cancelled),
|
||||
load: (target, cancelled) => loadTargetCalls(this.document, target, this.owner.indexed, cancelled),
|
||||
}
|
||||
},
|
||||
offset => this.requests.distance(this.document.positionAt(offset).line),
|
||||
@@ -820,7 +820,7 @@ class Preview {
|
||||
await this.checkRepoImages(msg.id, msg.paths)
|
||||
return
|
||||
case 'openCallTree':
|
||||
this.callTrees.open(msg.version, msg.anchor)
|
||||
this.callTrees.open(msg.version, msg.key)
|
||||
return
|
||||
case 'goToCallNode':
|
||||
await this.goToCallNode(Uri.parse(msg.uri), new Position(msg.line, msg.character))
|
||||
|
||||
Reference in new issue
Block a user