✨ feat(vscode): 代码阅读库规则卡片不再吞掉字段中间的方法,编辑时收起子块
This commit is contained in:
1 parent
c803fd457f
commit
2251e67ef8
15 files changed
+280
-54
No files matched your search
@@ -0,0 +1,21 @@
|
||||
import type { CodeBlock } from '../contract'
|
||||
|
||||
/** 库规则共用的挑子块的办法。 */
|
||||
|
||||
/** 紧挨在 child 前面的注释块(连着几块都算,中间没隔别的子块),照源码顺序:讲的是它下面的这个 child。 */
|
||||
export function leadingComments(children: readonly CodeBlock[], child: CodeBlock): CodeBlock[] {
|
||||
const at = children.indexOf(child)
|
||||
let from = at
|
||||
while (from > 0 && children[from - 1].kind === 'comment')
|
||||
from--
|
||||
return children.slice(from, at)
|
||||
}
|
||||
|
||||
/**
|
||||
* 要并进卡片的子块:成员连同各自前面紧挨着的注释块,照源码顺序。成员并进来了,讲它的注释跟着并,不在卡片下面单独留一块;
|
||||
* 讲别的子块(方法)的注释、最后一个子块后面的注释照常留下。
|
||||
*/
|
||||
export function withLeadingComments(children: readonly CodeBlock[], members: readonly CodeBlock[]): CodeBlock[] {
|
||||
return children.filter(child => members.includes(child))
|
||||
.flatMap(member => [...leadingComments(children, member), member])
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import type { BlockView } from '../blockView'
|
||||
import type { Inline, PropertyRow, Segment } from '../contract'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { toBlockViews } from '../blockView'
|
||||
import { replaceLines } from '../editText'
|
||||
import { createPythonAdapter } from '../languages/python'
|
||||
import { flatSegments, propertyLines, readFixture, WASM_DIR, withBlocks } from '../testSupport'
|
||||
import { pythonZhTranslator } from '../translators/pythonZh'
|
||||
@@ -122,7 +123,7 @@ describe('sqlAlchemy 库规则', () => {
|
||||
expect(view.children, view.id).toEqual([])
|
||||
})
|
||||
|
||||
it('并进来的行只在卡片里能编辑,卡片的编辑范围是整个类', async () => {
|
||||
it('卡片的编辑范围是整个类,每行只在一块里能编辑', async () => {
|
||||
const lines = SNAPSHOT.split('\n')
|
||||
const table = await model(SNAPSHOT, 'ReferralRelation')
|
||||
expect(lines[table.startLine]).toBe('class ReferralRelation(IntPK, TimestampMixin, Base):')
|
||||
@@ -137,6 +138,32 @@ describe('sqlAlchemy 库规则', () => {
|
||||
expect(owners.get(lines.indexOf(' __tablename__ = "referral_relations"'))).toEqual([table.id])
|
||||
})
|
||||
|
||||
it('两个字段中间写着的方法照常是子块,卡片编辑范围盖住它、写回整个类;字段前面的注释并进卡片', async () => {
|
||||
const source = [
|
||||
'from sqlalchemy.orm import Mapped, mapped_column',
|
||||
'',
|
||||
'',
|
||||
'class Order(Base):',
|
||||
' __tablename__ = "orders"',
|
||||
'',
|
||||
' # 金额',
|
||||
' amount: Mapped[int] = mapped_column(Integer)',
|
||||
'',
|
||||
' # 算税',
|
||||
' def tax(self) -> int:',
|
||||
' return self.amount // 10',
|
||||
'',
|
||||
' note: Mapped[str] = mapped_column(Text)',
|
||||
'',
|
||||
].join('\n')
|
||||
const [, order] = await views(source)
|
||||
expect(propertyLines(properties(order)).slice(-3)).toEqual(['字段: 2 个', ' amount | 整数 | | ', ' note | 长文本 | | '])
|
||||
expect(order.children.map(child => [child.kind, child.startLine])).toEqual([['comment', 9], ['function', 10]])
|
||||
expect([order.startLine, order.endLine]).toEqual([3, 13])
|
||||
expect(replaceLines(source, order.startLine, order.endLine, order.code)).toBe(source)
|
||||
expect(replaceLines(source, order.startLine, order.endLine, order.code.replace('// 10', '// 8'))).toBe(source.replace('// 10', '// 8'))
|
||||
})
|
||||
|
||||
it('跳到定义、高亮出现处:卡片自己是类名的定义,字段行是字段名的定义,带下标的词都指着源码里的这个名字', async () => {
|
||||
const table = await model(SNAPSHOT, 'ReferralRelation')
|
||||
expect(SNAPSHOT.slice(table.defines?.startIndex, table.defines?.endIndex)).toBe('ReferralRelation')
|
||||
@@ -162,7 +189,7 @@ describe('sqlAlchemy 库规则', () => {
|
||||
expect(table.attributes).toEqual({ kind: 'sqlalchemy.table', access: 'public' })
|
||||
})
|
||||
|
||||
it('import sqlalchemy as sa:老写法 Column、类型全名、只写注解的字段、不认得的类型照常翻、docstring 当说明;方法照常是子块,夹在中间的注释并进来', async () => {
|
||||
it('import sqlalchemy as sa:老写法 Column、类型全名、只写注解的字段、不认得的类型照常翻、docstring 当说明;方法照常是子块,字段前面的注释并进来', async () => {
|
||||
const source = [
|
||||
'import sqlalchemy as sa',
|
||||
'from sqlalchemy.orm import DeclarativeBase, relationship',
|
||||
@@ -205,11 +232,11 @@ describe('sqlAlchemy 库规则', () => {
|
||||
'关系: 1 个',
|
||||
' orders | Order | 对方字段 user · 单个对象',
|
||||
])
|
||||
expect([user.startLine, user.endLine]).toEqual([8, 18])
|
||||
expect([user.startLine, user.endLine]).toEqual([8, 21])
|
||||
expect(user.children.map(child => [child.kind, child.startLine])).toEqual([['function', 20]])
|
||||
})
|
||||
|
||||
it('字段的属性 docstring:没写 comment= 时当说明,写了以 comment= 为准;它并进卡片,编辑范围到最后一个字段的说明为止', async () => {
|
||||
it('字段的属性 docstring:没写 comment= 时当说明,写了以 comment= 为准;它并进卡片', async () => {
|
||||
const source = [
|
||||
'from sqlalchemy.orm import Mapped, mapped_column',
|
||||
'',
|
||||
@@ -234,7 +261,7 @@ describe('sqlAlchemy 库规则', () => {
|
||||
' summary | 长文本 | | 案例描述',
|
||||
' status | 字符串(16) | 可空 | 案例状态',
|
||||
])
|
||||
expect([table.startLine, table.endLine]).toEqual([3, 11])
|
||||
expect([table.startLine, table.endLine]).toEqual([3, 14])
|
||||
expect(table.children.map(child => [child.kind, child.startLine])).toEqual([['function', 13]])
|
||||
})
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import type { ClassBlock, CodeBlock, Inline, LibraryRule, Node, PropertyItem, PropertyRow, RuleBlock, RuleContext, ThemeColor, TokenRole } from '../contract'
|
||||
import type { Call } from './pythonTree'
|
||||
import { withLeadingComments } from './children'
|
||||
import { aliasesOf, callOf, canonicalName, lastName, stringValue, syntaxChildren } from './pythonTree'
|
||||
import { hint, joined, name, plain, range, word } from './segments'
|
||||
|
||||
/**
|
||||
* SQLAlchemy 库规则:声明式模型类(有 `__tablename__`,或主体里用 `mapped_column(...)` / `Column(...)` 定义字段)整块换成「数据表」卡片:
|
||||
* 名称 / 表名 / 说明 / 继承 / 装饰器,字段、关系、索引、唯一约束各一张表,都是有才显示。
|
||||
* `__tablename__`、`__table_args__`、字段和关系的赋值并进卡片,方法等别的成员照常是子块。
|
||||
* `__tablename__`、`__table_args__`、字段和关系的赋值(连同紧挨在它们前面的注释)并进卡片,方法等别的成员不管写在哪都照常是子块。
|
||||
* 规则只经 contract 拿信息,直接读的是 Python 语法树(tree-sitter-python 的节点类型);用词都在词表里,按翻译插件的显示语言挑。
|
||||
*/
|
||||
|
||||
@@ -449,7 +450,7 @@ function tableBlock(block: ClassBlock, model: Model, env: Env): RuleBlock {
|
||||
badge: { label: words.kind, color: TABLE_COLOR },
|
||||
rows,
|
||||
...(facts.name && { defines: range(facts.name) }),
|
||||
absorbs: members.filter((member): member is Member => member !== undefined).map(member => member.block),
|
||||
absorbs: withLeadingComments(block.children, members.filter((member): member is Member => member !== undefined).map(member => member.block)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user