🐛 fix(vscode): 代码阅读把 Python 赋值后面的字符串当变量说明,其余单独的字符串显示成注释

This commit is contained in:
cruldra committed 2026-09-26 18:57:07 +08:00
1 parent a51f90e34d
commit 1f86201373
14 files changed
+358 -59

No files matched your search

+77 -3
View File
@@ -1,7 +1,7 @@
import type { IterationFacts, Node } from '../contract'
import type { Outline } from '../testSupport'
import { describe, expect, it } from 'vitest'
import { factsByLine, nodesToText, outline, readFixture, runtime, WASM_DIR, withBlocks } from '../testSupport'
import { factsByLine, nodesToText, outline, readFixture, runtime, walk, WASM_DIR, withBlocks } from '../testSupport'
import { createPythonAdapter } from './python'
const adapter = createPythonAdapter(WASM_DIR)
@@ -79,15 +79,89 @@ describe('python 代码块树', () => {
])
})
it('docstring 只认主体开头;不是开头的字符串表达式是语句', async () => {
it('docstring 只认主体开头;不是开头、也没挂到赋值上的字符串是注释块,f-string、字节串照旧是语句', async () => {
const source = [
'def f():',
' x = 1',
' """not a docstring"""',
' "a" \'b\'',
' f"{x}"',
' b"x"',
' "a" f"b"',
].join('\n')
expect(await tree(source)).toEqual([
{ at: 1, kind: 'function', kids: [{ at: 2, kind: 'variable' }, { at: 3, kind: 'statement' }] },
{ at: 1, kind: 'function', kids: [
{ at: 2, kind: 'variable' },
{ at: 3, kind: 'comment' },
{ at: 4, kind: 'comment' },
{ at: 5, kind: 'statement' },
{ at: 6, kind: 'statement' },
{ at: 7, kind: 'statement' },
] },
])
expect(await tree('def f(): f"not a docstring"')).toEqual([{ at: 1, kind: 'function', kids: [{ at: 1, kind: 'statement' }] }])
})
})
describe('python 属性 docstring', () => {
/** 字段(一行译文的块)的说明,按 header 起始行(1 起)索引。 */
function fieldDocs(source: string): Promise<Record<number, unknown>> {
return withBlocks(adapter, 'python', source, (blocks) => {
const byLine: Record<number, unknown> = {}
walk(blocks, (block) => {
if (block.kind === 'field' && block.doc)
byLine[block.header.startRow + 1] = nodesToText(block.doc)
})
return byLine
})
}
it('attributeDocs.py:模块、类主体里紧跟在赋值后面的字符串并进那块,头部延到字符串结尾;隔着注释、不跟在赋值后面的是注释块', async () => {
expect(await tree(readFixture('attributeDocs.py'))).toEqual([
{ at: 1, kind: 'comment' },
{ at: [3, 4], kind: 'const' },
{ at: [6, 8], kind: 'variable' },
{ at: [11, 12], kind: 'class', kids: [
{ at: [14, 15], kind: 'variable' },
{ at: [17, 19], kind: 'field' },
{ at: 21, kind: 'comment' },
{ at: 22, kind: 'comment' },
] },
{ at: 25, kind: 'function', kids: [
{ at: 26, kind: 'variable' },
{ at: [27, 30], kind: 'comment' },
{ at: 31, kind: 'statement' },
{ at: 32, kind: 'statement' },
{ at: 33, kind: 'return' },
] },
])
})
it('进常量、变量的 facts.doc 和字段的 doc,是那个字符串节点;函数体里的赋值没有', async () => {
const source = readFixture('attributeDocs.py')
const found = await facts(source)
expect(found[3]).toMatchObject({ doc: '"""一 deployment 一案例的唯一约束名; 入库侧按它认"重复入库", 故与建表同源。"""' })
expect(found[6]).toMatchObject({ doc: '"""隔着空行、赋值行尾有注释也算。"""' })
expect(found[14]).toMatchObject({ doc: '"""正在分析源 deployment."""' })
expect(found[26]).not.toHaveProperty('doc')
expect(await fieldDocs(source)).toEqual({ 17: '"""案例标题,\n 用户可编辑."""' })
})
it('只挂到紧跟着的下一条语句:两个字符串只有第一个是说明;没有值的注解、拼接的字符串也算;条件分支里的赋值不算', async () => {
const source = [
'class K:',
' a: int',
' "A" "B"',
' "又一个"',
'if DEBUG:',
' X = 1',
' """不算"""',
].join('\n')
expect(await tree(source)).toEqual([
{ at: 1, kind: 'class', kids: [{ at: [2, 3], kind: 'field' }, { at: 4, kind: 'comment' }] },
{ at: 5, kind: 'if', kids: [{ at: 6, kind: 'variable' }, { at: 7, kind: 'comment' }] },
])
expect(await fieldDocs(source)).toEqual({ 2: '"A" "B"' })
})
})