✨ feat(vscode): 代码阅读把说明里的 @param / @return / @throws 拆到参数表、输出、异常表
This commit is contained in:
1 parent
c2982537d7
commit
0958506681
43 files changed
+1300
-323
No files matched your search
@@ -1,5 +1,7 @@
|
||||
import type { FunctionBlock, ImportFacts, IndexEntry, Inline, KindIconName, LibraryRule, Node, ParameterFacts, PropertyItem, PropertyRow, RuleCard, RuleContext, Segment, ThemeColor, VariableBlock, WorkspaceIndex } from '../contract'
|
||||
import type { FunctionBlock, ImportFacts, IndexEntry, Inline, KindIconName, LibraryRule, Node, ParameterFacts, PreviewText, PropertyItem, PropertyRow, RuleCard, RuleContext, Segment, ThemeColor, VariableBlock, WorkspaceIndex } from '../contract'
|
||||
import type { ParamDocs } from './docRows'
|
||||
import type { Call, LibraryNames } from './pythonTree'
|
||||
import { docBodyRows, paramDocs, returnNote, throwsRows, unmatchedParamRows } from './docRows'
|
||||
import { callOf, genericOf, lastName, libraryName, stringContent, syntaxChildren, unwrapType } from './pythonTree'
|
||||
import { hint, joined, name, range, word } from './segments'
|
||||
|
||||
@@ -74,6 +76,8 @@ interface Words {
|
||||
no: string
|
||||
count: (n: number) => string
|
||||
requestHeads: string[]
|
||||
/** 请求表里有参数写了说明(`@param`)时多出的那一列 */
|
||||
requestDoc: string
|
||||
responseHeads: string[]
|
||||
/** 路由器卡片的接口表:方法 | 路径 | 函数 | 说明 */
|
||||
routeHeads: string[]
|
||||
@@ -113,6 +117,7 @@ const ZH: Words = {
|
||||
no: '否',
|
||||
count: n => `${n} 个`,
|
||||
requestHeads: ['项', '值'],
|
||||
requestDoc: '说明',
|
||||
responseHeads: ['状态码', '内容', '说明'],
|
||||
routeHeads: ['方法', '路径', '函数', '说明'],
|
||||
items: { url: 'URL', method: '方法', path: '路径参数', query: '查询参数', header: '请求头', cookie: 'Cookie', body: '请求体' },
|
||||
@@ -325,11 +330,11 @@ function parametersOf(definition: Node): Map<string, Node> {
|
||||
return new Map(names.flatMap(param => param?.type === 'identifier' ? [[param.text, param] as const] : []))
|
||||
}
|
||||
|
||||
/** 函数 docstring 的第一行(翻译插件取的说明原文),没有 docstring 时没有。 */
|
||||
/** 函数 docstring 正文的第一行(翻译插件读出的说明,块标签已经拿掉),没有 docstring 时没有。 */
|
||||
function docFirstLine(definition: Node, env: Env): string | undefined {
|
||||
const [first] = syntaxChildren(definition.childForFieldName('body'))
|
||||
const [doc] = first?.type === 'expression_statement' ? syntaxChildren(first) : []
|
||||
const markdown = doc?.type === 'string' ? env.context.doc(doc)?.markdown : undefined
|
||||
const markdown = doc?.type === 'string' ? env.context.doc(doc)?.body?.markdown : undefined
|
||||
return markdown?.split('\n').map(line => line.trim()).find(line => line !== '')
|
||||
}
|
||||
|
||||
@@ -624,9 +629,37 @@ function methodText(method: string): Inline[] {
|
||||
return [{ text: method, role: 'keyword', ...(METHOD_COLORS[method] && { color: METHOD_COLORS[method] }) }]
|
||||
}
|
||||
|
||||
/** 请求表的一行:项 | 值。 */
|
||||
function requestItem(label: string, value: Inline[]): PropertyItem {
|
||||
return { cells: [[hint(label)], value] }
|
||||
/** 请求表的一行:项 | 值,表里有说明一列时再加说明(note,没有说明的是空的)。 */
|
||||
function requestItem(label: string, value: Inline[], note?: PreviewText): PropertyItem {
|
||||
return { cells: [[hint(label)], value, ...(note ? [note] : [])] }
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求表的各项:URL、方法,然后路径参数、查询参数、请求头、Cookie、请求体,有才列。说明里有请求参数对上了 `@param`(docs)时
|
||||
* described:表多一列说明,这几种参数一个参数一行、各带它的说明;没有时同一种参数逗号隔开写在一行。
|
||||
*/
|
||||
function requestItems(url: Inline[], method: string, placed: readonly Placed[], docs: ParamDocs, env: Env): { items: PropertyItem[], described: boolean } {
|
||||
const { words, context } = env
|
||||
const described = placed.some(item => item.place !== 'dependency' && docs.of(item.param.text))
|
||||
const item = (label: string, value: Inline[], note?: PreviewText): PropertyItem => requestItem(label, value, described ? note ?? [] : undefined)
|
||||
const at = (place: Place): Placed[] => placed.filter(one => one.place === place)
|
||||
const listed = (place: Exclude<Place, 'dependency'>): PropertyItem[] => {
|
||||
const found = at(place)
|
||||
if (described)
|
||||
return found.map(one => item(words.items[place], paramsText([one], env), docs.of(one.param.text)))
|
||||
return found.length > 0 ? [item(words.items[place], paramsText(found, env))] : []
|
||||
}
|
||||
const body = at('body')
|
||||
const [only] = body
|
||||
return {
|
||||
items: [
|
||||
item(words.items.url, url),
|
||||
item(words.items.method, methodText(method)),
|
||||
...(['path', 'query', 'header', 'cookie'] as const).flatMap(listed),
|
||||
...(body.length === 1 && only?.type ? [item(words.items.body, context.translate(only.type), docs.of(only.param.text))] : listed('body')),
|
||||
],
|
||||
described,
|
||||
}
|
||||
}
|
||||
|
||||
function routeCard(block: FunctionBlock, route: Route, env: Env): RuleCard {
|
||||
@@ -639,16 +672,12 @@ function routeCard(block: FunctionBlock, route: Route, env: Env): RuleCard {
|
||||
const inPath = pathNames([router.prefix, route.path])
|
||||
const placed = facts.parameters.flatMap(parameter => placeOf(parameter, inPath, env) ?? [])
|
||||
const at = (place: Place): Placed[] => placed.filter(item => item.place === place)
|
||||
const body = at('body')
|
||||
|
||||
const params = new Map(facts.parameters.flatMap(({ name: param }) => param.type === 'identifier' ? [[param.text, param] as const] : []))
|
||||
const url = [...pathSegments(router.prefix, params, env), ...pathSegments(route.path, params, env)]
|
||||
const request: PropertyItem[] = [
|
||||
requestItem(words.items.url, url),
|
||||
requestItem(words.items.method, methodText(route.method)),
|
||||
...(['path', 'query', 'header', 'cookie'] as const).flatMap(place => at(place).length > 0 ? [requestItem(words.items[place], paramsText(at(place), env))] : []),
|
||||
...(body.length > 0 ? [requestItem(words.items.body, body.length === 1 && body[0].type ? context.translate(body[0].type) : paramsText(body, env))] : []),
|
||||
]
|
||||
const doc = facts.doc && context.doc(facts.doc)
|
||||
const docs = paramDocs(doc, facts.parameters.map(parameter => parameter.name.text))
|
||||
const request = requestItems(url, route.method, placed, docs, env)
|
||||
|
||||
const statuses = new Statuses()
|
||||
const success = call.keywords.get('status_code')
|
||||
@@ -668,7 +697,6 @@ function routeCard(block: FunctionBlock, route: Route, env: Env): RuleCard {
|
||||
if (placed.some(item => item.place !== 'dependency'))
|
||||
statuses.add(numericStatus('422'), [hint(words.validation)], [hint(words.automatic)])
|
||||
|
||||
const doc = facts.doc && context.doc(facts.doc)
|
||||
const tags = [...router.tags, ...listItems(call.keywords.get('tags'))].map(tag => tagText(tag, env))
|
||||
const decorators = facts.decorators.filter(other => other !== route.decorator)
|
||||
const dependencies = [
|
||||
@@ -680,7 +708,8 @@ function routeCard(block: FunctionBlock, route: Route, env: Env): RuleCard {
|
||||
]
|
||||
const rows: PropertyRow[] = [
|
||||
...(facts.name ? [{ label: labels.name, value: [name(facts.name, 'function')] }] : []),
|
||||
...(doc ? [{ label: labels.doc, value: doc }] : []),
|
||||
...docBodyRows(doc, labels.doc),
|
||||
...unmatchedParamRows(docs.unmatched),
|
||||
{ label: labels.router, value: [name(router.target, 'variable')] },
|
||||
...(tags.length > 0 ? [{ label: labels.tags, value: joined(tags, separator) }] : []),
|
||||
{ label: labels.async, value: [word(facts.async ? words.yes : words.no)] },
|
||||
@@ -689,10 +718,11 @@ function routeCard(block: FunctionBlock, route: Route, env: Env): RuleCard {
|
||||
{
|
||||
label: labels.request,
|
||||
value: [hint(`${route.method} ${textOf(url)}`)],
|
||||
columns: words.requestHeads,
|
||||
items: request,
|
||||
columns: request.described ? [...words.requestHeads, words.requestDoc] : words.requestHeads,
|
||||
items: request.items,
|
||||
},
|
||||
responsesRow(labels.responses, statuses, env),
|
||||
{ ...responsesRow(labels.responses, statuses, env), ...returnNote(doc) },
|
||||
...throwsRows(doc),
|
||||
]
|
||||
return {
|
||||
kind: KIND,
|
||||
@@ -712,7 +742,7 @@ function routerCard(block: VariableBlock, router: Router, routes: readonly Route
|
||||
const { context, words } = env
|
||||
const { labels } = words
|
||||
const separator = hint(words.listSeparator)
|
||||
const doc = block.facts.doc && context.doc(block.facts.doc)
|
||||
const doc = block.facts.doc && context.doc(block.facts.doc)?.body
|
||||
const tags = router.tags.map(tag => tagText(tag, env))
|
||||
const dependencies = listedDependencies(router.dependencies, env)
|
||||
const statuses = new Statuses()
|
||||
|
||||
Reference in new issue
Block a user