✨ feat(vscode): tauri、axum 的链式调用卡片换成库自己的叫法,Router 列出全部路由
规则按起点认链、用 RuleContext.chain 带上读法:tauri::Builder 连着的 .plugin 合成「插件 N 个」表(插件 | 配置),setup / on_window_event / manage 叫启动时 / 窗口事件 / 托管状态,invoke_handler 列出命令名,build → expect → run 合成「构建并运行」;axum Router 连着的 .route 合成路由表(路径 | 方法 | 处理函数,每条都列),with_state、layer、nest 等起叫法;valueCard 交出语句的式子
This commit is contained in:
1 parent
6ad2a5b27a
commit
aaa695eae4
4 files changed
+335
-37
No files matched your search
@@ -1,16 +1,29 @@
|
||||
import type { FunctionBlock, Inline, KindBadge, LibraryRule, Node, PropertyItem, PropertyRow, RuleCard, RuleContext, ThemeColor } from '../contract'
|
||||
import type { ChainGroup, ChainStepFacts, FunctionBlock, Inline, KindBadge, LibraryRule, Node, PropertyItem, PropertyRow, RuleCard, RuleContext, ThemeColor } from '../contract'
|
||||
import { returnNote, throwsRows, writtenParamRows } from './docRows'
|
||||
import { returnValue, valueCard } from './rustRows'
|
||||
import { chainRows, methodRuns, returnValue, valueCard } from './rustRows'
|
||||
import { attributeName, docSummary, named, stringContent } from './rustTree'
|
||||
|
||||
/** 和 FastAPI 接口同一个蓝。 */
|
||||
const COLOR: ThemeColor = { dark: '#61afef', light: '#1a6fb5' }
|
||||
const BADGE: KindBadge = { label: '接口', color: COLOR, icon: 'globe' }
|
||||
/** 和 FastAPI 路由器同一个蓝、同一个图标。 */
|
||||
const ROUTER_BADGE: KindBadge = { label: '路由器', color: { dark: '#7aa2f7', light: '#34548a' }, icon: 'network' }
|
||||
const METHODS = new Set(['get', 'post', 'put', 'delete', 'patch', 'head', 'options', 'trace'])
|
||||
|
||||
/** Router 链里这几个方法的叫法;连着调用几次的合成一步。连着的 `.route` 另合成路由表(见 [routerGroups])。 */
|
||||
const STEP_NAMES: Record<string, string> = {
|
||||
with_state: '共享状态',
|
||||
layer: '中间件',
|
||||
route_layer: '路由中间件',
|
||||
nest: '嵌套路由',
|
||||
nest_service: '嵌套服务',
|
||||
merge: '合并路由',
|
||||
fallback: '兜底处理',
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理函数上的 get / post 等路由属性,或语句、变量的初始值、返回值里 Router 上能看出来的 route。
|
||||
* 请求表和 FastAPI 接口一样:方法、路径、路径参数,再加返回。
|
||||
* 处理函数上的 get / post 等路由属性;语句、变量的初始值、返回值是 Router 的链时排成「路由器」的链式调用卡片,每条路由都列出来
|
||||
* (见 [routerChainCard]);只写了一步 `.route` 的照旧是那一个接口的卡片。请求表和 FastAPI 接口一样:方法、路径、路径参数,再加返回。
|
||||
* 文件里导入了 axum 才启用。
|
||||
*/
|
||||
export const axumRule: LibraryRule = {
|
||||
@@ -24,11 +37,67 @@ export const axumRule: LibraryRule = {
|
||||
return route ? card(block, route, context) : undefined
|
||||
}
|
||||
return valueCard(block, context, (value, own) => {
|
||||
const chained = routerChainCard(value, context)
|
||||
if (chained)
|
||||
return chained
|
||||
const [route] = routesOn(value, own).filter(item => !handled(value, item.handler))
|
||||
return route ? routerCard(value, route, context) : undefined
|
||||
})
|
||||
},
|
||||
attributeNames: () => ({ attributes: {}, values: { kind: { 'axum.route': '接口' } } }),
|
||||
attributeNames: () => ({ attributes: {}, values: { kind: { 'axum.route': '接口', 'axum.router': '路由器' } } }),
|
||||
}
|
||||
|
||||
/**
|
||||
* Router 的链(起点是 `Router::new()` / `Router::default()`,或链里有 `.route`)排成链式调用卡片:连着的 `.route` 合成路由表,
|
||||
* 别的几步按 [STEP_NAMES] 起叫法;不是这种链时没有。
|
||||
*/
|
||||
function routerChainCard(value: Node, context: RuleContext): RuleCard | undefined {
|
||||
const facts = context.document.language.chain?.(value)
|
||||
const start = facts?.start.text.replace(/\s+/g, '')
|
||||
if (!facts || !(/^(?:axum::)?Router::(?:new|default)\(\)$/.test(start ?? '') || facts.steps.some(step => step.method.text === 'route')))
|
||||
return undefined
|
||||
const view = context.chain(value, routerGroups(facts.steps, context))
|
||||
return view && { kind: 'axum.router', badge: ROUTER_BADGE, rows: chainRows(view), absorbs: [] }
|
||||
}
|
||||
|
||||
/** 连着的 `.route` 合成「路由 N 个」表(路径 | 方法 | 处理函数,一个路径挂几个方法的各占一行),别的按 [STEP_NAMES] 起叫法。 */
|
||||
function routerGroups(steps: readonly ChainStepFacts[], context: RuleContext): ChainGroup[] {
|
||||
return methodRuns(steps).flatMap(({ from, to, method }): ChainGroup[] => {
|
||||
if (method === 'route') {
|
||||
const items = steps.slice(from, to + 1).flatMap(step => routeItems(step, context))
|
||||
return [{ from, to, label: `路由 ${items.length} 个`, table: { columns: ['路径', '方法', '处理函数'], items } }]
|
||||
}
|
||||
const label = STEP_NAMES[method]
|
||||
return label ? [{ from, to, label }] : []
|
||||
})
|
||||
}
|
||||
|
||||
/** 一步 `.route(路径, get(a).post(b))` 的各行:路径去掉引号、带源码下标,方法大写,处理函数是名字时按函数着色、带源码下标。 */
|
||||
function routeItems(step: ChainStepFacts, context: RuleContext): PropertyItem[] {
|
||||
const [path, router] = step.arguments
|
||||
const content = path?.node.children.find(child => child?.type === 'string_content')
|
||||
const pathCell: Inline[] = content ? [{ text: content.text, role: 'string', offset: content.startIndex }] : path ? context.verbatim(path.node) : [hint('—')]
|
||||
return methodRoutes(router?.node).map(({ method, handler }) => ({
|
||||
cells: [
|
||||
pathCell,
|
||||
[keyword(method.toUpperCase())],
|
||||
handler?.type === 'identifier' ? [{ text: handler.text, role: 'function', offset: handler.startIndex }] : handler ? context.verbatim(handler) : [hint('—')],
|
||||
],
|
||||
}))
|
||||
}
|
||||
|
||||
/** 方法路由 `get(a)`、`get(a).post(b)` 里的各个方法和它的处理函数,照源码顺序;认不出时是空的。 */
|
||||
function methodRoutes(node: Node | undefined): { method: string, handler?: Node }[] {
|
||||
if (node?.type !== 'call_expression')
|
||||
return []
|
||||
const fn = node.childForFieldName('function')
|
||||
const handler = named(node.childForFieldName('arguments')).find(arg => !arg.isExtra)
|
||||
if (fn?.type === 'field_expression') {
|
||||
const method = fn.childForFieldName('field')?.text
|
||||
return method && METHODS.has(method) ? [...methodRoutes(fn.childForFieldName('value') ?? undefined), { method, handler }] : []
|
||||
}
|
||||
const method = fn?.text.split('::').pop()
|
||||
return method && METHODS.has(method) ? [{ method, handler }] : []
|
||||
}
|
||||
|
||||
interface Route {
|
||||
|
||||
Reference in new issue
Block a user