🐛 fix(vscode): 实参里的闭包、async 块写了查询或路由时不再外面那句、子块各出一张卡
valueCard 交给规则一个 own:拆成子块的那几段不归这一块,sqlx 找查询、axum 找路由时跳过;顺带去掉 rustRows 里 nameRow 没用的参数(页面那边的类型检查报它)
This commit is contained in:
1 parent
1956879aa1
commit
6ad2a5b27a
4 files changed
+53
-20
No files matched your search
@@ -23,8 +23,8 @@ export const axumRule: LibraryRule = {
|
|||||||
const route = attributeRoute(block)
|
const route = attributeRoute(block)
|
||||||
return route ? card(block, route, context) : undefined
|
return route ? card(block, route, context) : undefined
|
||||||
}
|
}
|
||||||
return valueCard(block, context, (value) => {
|
return valueCard(block, context, (value, own) => {
|
||||||
const [route] = routesOn(value).filter(item => !handled(value, item.handler))
|
const [route] = routesOn(value, own).filter(item => !handled(value, item.handler))
|
||||||
return route ? routerCard(value, route, context) : undefined
|
return route ? routerCard(value, route, context) : undefined
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -116,7 +116,10 @@ function requestRow(route: Route, params: Inline[][]): PropertyRow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function routesOn(node: Node, found: (Route & { handlerNode?: Node })[] = []): (Route & { handlerNode?: Node })[] {
|
/** 式子里写的各个 `.route(路径, 方法(函数))`,外层在前;拆成子块的那几段(`own` 为假)不看。 */
|
||||||
|
function routesOn(node: Node, own: (node: Node) => boolean, found: (Route & { handlerNode?: Node })[] = []): (Route & { handlerNode?: Node })[] {
|
||||||
|
if (!own(node))
|
||||||
|
return found
|
||||||
if (node.type === 'call_expression') {
|
if (node.type === 'call_expression') {
|
||||||
const callee = node.childForFieldName('function')
|
const callee = node.childForFieldName('function')
|
||||||
const field = callee?.type === 'field_expression' ? callee.childForFieldName('field')?.text : undefined
|
const field = callee?.type === 'field_expression' ? callee.childForFieldName('field')?.text : undefined
|
||||||
@@ -137,7 +140,7 @@ function routesOn(node: Node, found: (Route & { handlerNode?: Node })[] = []): (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const child of named(node))
|
for (const child of named(node))
|
||||||
routesOn(child, found)
|
routesOn(child, own, found)
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -279,3 +279,28 @@ describe('函数体里的 sqlx 查询、axum 路由', () => {
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('实参里的闭包、async 块拆成子块时', () => {
|
||||||
|
it('里面写的查询、路由只出在子块那一张卡上,外面那句不再跟着认成查询、路由', async () => {
|
||||||
|
const source = [
|
||||||
|
'use axum::Router;',
|
||||||
|
'use axum::routing::get;',
|
||||||
|
'use sqlx::PgPool;',
|
||||||
|
'',
|
||||||
|
'async fn start(pool: PgPool) {',
|
||||||
|
' tokio::spawn(async move {',
|
||||||
|
' sqlx::query!("DELETE FROM jobs").execute(&pool).await;',
|
||||||
|
' });',
|
||||||
|
' rows.iter().for_each(|row| {',
|
||||||
|
' let app = Router::new().route("/health", get(health));',
|
||||||
|
' });',
|
||||||
|
'}',
|
||||||
|
'',
|
||||||
|
].join('\n')
|
||||||
|
const [start] = (await views(source)).filter(block => block.kind === 'function')
|
||||||
|
expect((start?.children ?? []).map(block => [block.kind, block.children.map(child => child.kind)])).toEqual([
|
||||||
|
['statement', ['sqlx.query']],
|
||||||
|
['statement', ['axum.route']],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -55,7 +55,7 @@ export function structRows(block: ClassBlock, say: RustSay): PropertyRow[] {
|
|||||||
const docs = paramDocs(doc, fields.map(field => field.name.text))
|
const docs = paramDocs(doc, fields.map(field => field.name.text))
|
||||||
const described = fields.some(field => docs.of(field.name.text))
|
const described = fields.some(field => docs.of(field.name.text))
|
||||||
return [
|
return [
|
||||||
nameRow(block.facts.name, say, 'type'),
|
nameRow(block.facts.name, 'type'),
|
||||||
...docRow(doc),
|
...docRow(doc),
|
||||||
...unmatchedParamRows(docs.unmatched),
|
...unmatchedParamRows(docs.unmatched),
|
||||||
visibilityRow(block),
|
visibilityRow(block),
|
||||||
@@ -73,7 +73,7 @@ export function structRows(block: ClassBlock, say: RustSay): PropertyRow[] {
|
|||||||
|
|
||||||
export function moduleRows(block: ModuleBlock, say: RustSay): PropertyRow[] {
|
export function moduleRows(block: ModuleBlock, say: RustSay): PropertyRow[] {
|
||||||
return [
|
return [
|
||||||
nameRow(block.facts.name, say, 'variable'),
|
nameRow(block.facts.name, 'variable'),
|
||||||
...docRow(block.facts.doc && say.doc(block.facts.doc)),
|
...docRow(block.facts.doc && say.doc(block.facts.doc)),
|
||||||
visibilityRow(block),
|
visibilityRow(block),
|
||||||
]
|
]
|
||||||
@@ -172,7 +172,7 @@ function cfgText(predicate: CfgPredicate, nested: boolean): Inline[] {
|
|||||||
|
|
||||||
export function enumRows(block: EnumBlock, say: RustSay): PropertyRow[] {
|
export function enumRows(block: EnumBlock, say: RustSay): PropertyRow[] {
|
||||||
return [
|
return [
|
||||||
nameRow(block.facts.name, say, 'type'),
|
nameRow(block.facts.name, 'type'),
|
||||||
...docRow(block.facts.doc && say.doc(block.facts.doc)),
|
...docRow(block.facts.doc && say.doc(block.facts.doc)),
|
||||||
...tableRow('变体', ['变体', '携带的数据'], block.facts.members.map(member => ({
|
...tableRow('变体', ['变体', '携带的数据'], block.facts.members.map(member => ({
|
||||||
cells: [[sourceName(member.name, 'type')], payloadValue(member.value, say)],
|
cells: [[sourceName(member.name, 'type')], payloadValue(member.value, say)],
|
||||||
@@ -192,7 +192,7 @@ export function functionRows(block: FunctionBlock, say: RustSay): PropertyRow[]
|
|||||||
const docs = paramDocs(doc, parameters.map(param => param.name.text))
|
const docs = paramDocs(doc, parameters.map(param => param.name.text))
|
||||||
const described = parameters.some(param => docs.of(param.name.text))
|
const described = parameters.some(param => docs.of(param.name.text))
|
||||||
return [
|
return [
|
||||||
nameRow(block.facts.name, say, 'function'),
|
nameRow(block.facts.name, 'function'),
|
||||||
...docRow(doc),
|
...docRow(doc),
|
||||||
...unmatchedParamRows(docs.unmatched),
|
...unmatchedParamRows(docs.unmatched),
|
||||||
...(block.facts.async ? [{ label: '异步', value: [keyword('是')] }] : []),
|
...(block.facts.async ? [{ label: '异步', value: [keyword('是')] }] : []),
|
||||||
@@ -212,7 +212,7 @@ export function functionRows(block: FunctionBlock, say: RustSay): PropertyRow[]
|
|||||||
|
|
||||||
export function traitRows(block: TraitBlock, say: RustSay): PropertyRow[] {
|
export function traitRows(block: TraitBlock, say: RustSay): PropertyRow[] {
|
||||||
return [
|
return [
|
||||||
nameRow(block.facts.name, say, 'type'),
|
nameRow(block.facts.name, 'type'),
|
||||||
...docRow(block.facts.doc && say.doc(block.facts.doc)),
|
...docRow(block.facts.doc && say.doc(block.facts.doc)),
|
||||||
...methodRows(block, say),
|
...methodRows(block, say),
|
||||||
]
|
]
|
||||||
@@ -245,7 +245,7 @@ function methodRows(block: TraitBlock | ImplBlock, say: RustSay): PropertyRow[]
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
function nameRow(name: Node | undefined, say: RustSay, role: TokenRole): PropertyRow {
|
function nameRow(name: Node | undefined, role: TokenRole): PropertyRow {
|
||||||
return {
|
return {
|
||||||
label: '名称',
|
label: '名称',
|
||||||
value: name ? [sourceName(name, role)] : [hint('匿名')],
|
value: name ? [sourceName(name, role)] : [hint('匿名')],
|
||||||
@@ -296,22 +296,25 @@ export function payloadValue(body: Node | undefined, say: RustSay): Inline[] {
|
|||||||
* Rust 库规则认函数体里的一块时看它算出的值:语句看整句,变量看初始值,返回块看返回的值,交给 `card` 认成卡片。
|
* Rust 库规则认函数体里的一块时看它算出的值:语句看整句,变量看初始值,返回块看返回的值,交给 `card` 认成卡片。
|
||||||
*
|
*
|
||||||
* 认领变量时变量名排在卡片第一行「变量」,卡片和这一行的定义范围都是它,同没认领时的变量块;
|
* 认领变量时变量名排在卡片第一行「变量」,卡片和这一行的定义范围都是它,同没认领时的变量块;
|
||||||
* `let … else` 的 else、实参里的闭包这些子块不并进卡片,照常是它的子块。
|
* `let … else` 的 else、实参里的闭包这些子块不并进卡片,照常是它的子块。拆成子块的那几段讲在子块自己的卡片上,
|
||||||
|
* `card` 在值里找东西时跳过它们(`own` 为假的节点),同一句查询、路由不会外面那句、子块各出一张卡。
|
||||||
*
|
*
|
||||||
* @param block 要认的那一块
|
* @param block 要认的那一块
|
||||||
* @param context 把变量名翻成一行用
|
* @param context 把变量名翻成一行用
|
||||||
* @param card 从值里认出卡片,认不出是 `undefined`
|
* @param card 从值里认出卡片,认不出是 `undefined`;`own` 说一个节点是不是这一块自己的(不在哪个子块里)
|
||||||
* @return 认出的卡片;别的种类的块、没写初始值的变量是 `undefined`
|
* @return 认出的卡片;别的种类的块、没写初始值的变量是 `undefined`
|
||||||
*/
|
*/
|
||||||
export function valueCard(block: CodeBlock, context: RuleContext, card: (value: Node) => RuleCard | undefined): RuleCard | undefined {
|
export function valueCard(block: CodeBlock, context: RuleContext, card: (value: Node, own: (node: Node) => boolean) => RuleCard | undefined): RuleCard | undefined {
|
||||||
|
// 子块从头部开头(else 子块从 else 这个词)到它的节点结尾;else 的节点是整条 let,不能拿节点的开头算
|
||||||
|
const own = (node: Node): boolean => !block.children.some(child => node.startIndex >= child.header.startIndex && node.endIndex <= child.node.endIndex)
|
||||||
if (block.kind === 'statement' || block.kind === 'return')
|
if (block.kind === 'statement' || block.kind === 'return')
|
||||||
return card(block.node)
|
return card(block.node, own)
|
||||||
if (block.kind !== 'variable')
|
if (block.kind !== 'variable')
|
||||||
return undefined
|
return undefined
|
||||||
const [declarator, ...rest] = block.facts.declarators
|
const [declarator, ...rest] = block.facts.declarators
|
||||||
if (!declarator?.value || rest.length > 0)
|
if (!declarator?.value || rest.length > 0)
|
||||||
return undefined
|
return undefined
|
||||||
const found = card(declarator.value)
|
const found = card(declarator.value, own)
|
||||||
if (!found)
|
if (!found)
|
||||||
return undefined
|
return undefined
|
||||||
const defines = range(declarator.name)
|
const defines = range(declarator.name)
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ export const sqlxRule: LibraryRule = {
|
|||||||
label: 'sqlx 查询',
|
label: 'sqlx 查询',
|
||||||
languageIds: ['rust'],
|
languageIds: ['rust'],
|
||||||
libraries: ['sqlx'],
|
libraries: ['sqlx'],
|
||||||
claim: (block, context) => valueCard(block, context, (value) => {
|
claim: (block, context) => valueCard(block, context, (value, own) => {
|
||||||
const query = findQuery(value)
|
const query = findQuery(value, own)
|
||||||
return query && card(query, context)
|
return query && card(query, context)
|
||||||
}),
|
}),
|
||||||
attributeNames: () => ({ attributes: {}, values: { kind: { 'sqlx.query': '查询' } } }),
|
attributeNames: () => ({ attributes: {}, values: { kind: { 'sqlx.query': '查询' } } }),
|
||||||
@@ -51,13 +51,15 @@ function sqlText(sql: Node | undefined, context: RuleContext): Inline[] {
|
|||||||
return [{ text: text ?? '', role: 'string', ...(sql && { offset: sql.startIndex }) }]
|
return [{ text: text ?? '', role: 'string', ...(sql && { offset: sql.startIndex }) }]
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 外层先认,找到的第一个查询。 */
|
/** 外层先认,找到的第一个查询;拆成子块的那几段(`own` 为假)不看。 */
|
||||||
function findQuery(node: Node): Query | undefined {
|
function findQuery(node: Node, own: (node: Node) => boolean): Query | undefined {
|
||||||
|
if (!own(node))
|
||||||
|
return undefined
|
||||||
const query = macroQuery(node) ?? callQuery(node)
|
const query = macroQuery(node) ?? callQuery(node)
|
||||||
if (query)
|
if (query)
|
||||||
return query
|
return query
|
||||||
for (const child of named(node)) {
|
for (const child of named(node)) {
|
||||||
const found = findQuery(child)
|
const found = findQuery(child, own)
|
||||||
if (found)
|
if (found)
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user