🐛 fix(vscode): 注释里不带路径的引用从被注释的声明起一层层往外找,同类的方法、类的成员都能链到
This commit is contained in:
1 parent
15972c0b35
commit
5120e50228
15 files changed
+450
-167
No files matched your search
@@ -1,9 +1,10 @@
|
||||
import type { BlockView } from './blockView'
|
||||
import type { ReferenceSources, SymbolNode } from './symbolLinks'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { createPythonAdapter } from './languages/python'
|
||||
import { createTypeScriptAdapter } from './languages/typescript'
|
||||
import { importsIn } from './libraryRules'
|
||||
import { importedNames, resolveReference, splitReference } from './symbolLinks'
|
||||
import { importedNames, referenceScope, resolveReference, splitReference } from './symbolLinks'
|
||||
import { readFixture, WASM_DIR, withBlocks } from './testSupport'
|
||||
|
||||
const py = createPythonAdapter(WASM_DIR)
|
||||
@@ -40,14 +41,14 @@ describe('不带路径的引用:指到哪', () => {
|
||||
|
||||
it('本文件定义的优先于导入的;不问语言扩展', async () => {
|
||||
const found = sources({ 'file:///here.py': [symbol('Agent', 100, 200)] }, { 20: { uri: 'file:///models.py', at: 5, length: 5 } })
|
||||
expect(await resolveReference('Agent', imports, found)).toEqual({ uri: 'file:///here.py', selection: { startIndex: 100, endIndex: 105 } })
|
||||
expect(await resolveReference('Agent', undefined, imports, found)).toEqual({ uri: 'file:///here.py', selection: { startIndex: 100, endIndex: 105 } })
|
||||
expect(found.asked).toEqual([])
|
||||
})
|
||||
|
||||
it('本文件的 Class.member 在类底下找成员;类有、成员没有就是找不到,不再去导入里找', async () => {
|
||||
const here = sources({ 'file:///here.py': [symbol('Agent', 100, 200, [symbol('owner_id', 120, 140)])] }, { 20: { uri: 'file:///models.py', at: 5, length: 5 } })
|
||||
expect(await resolveReference('Agent.owner_id', imports, here)).toEqual({ uri: 'file:///here.py', selection: { startIndex: 120, endIndex: 128 } })
|
||||
expect(await resolveReference('Agent.missing', imports, here)).toBeUndefined()
|
||||
expect(await resolveReference('Agent.owner_id', undefined, imports, here)).toEqual({ uri: 'file:///here.py', selection: { startIndex: 120, endIndex: 128 } })
|
||||
expect(await resolveReference('Agent.missing', undefined, imports, here)).toBeUndefined()
|
||||
expect(here.asked).toEqual([])
|
||||
})
|
||||
|
||||
@@ -57,21 +58,101 @@ describe('不带路径的引用:指到哪', () => {
|
||||
'file:///models.py': [symbol('Other', 0, 4, [symbol('owner_id', 1, 3)]), symbol('Agent', 10, 90, [symbol('owner_id', 30, 50)])],
|
||||
}
|
||||
const found = sources(files, { 20: { uri: 'file:///models.py', at: 10, length: 5 } })
|
||||
expect(await resolveReference('Agent', imports, found)).toEqual({ uri: 'file:///models.py', selection: { startIndex: 10, endIndex: 15 } })
|
||||
expect(await resolveReference('Agent.owner_id', imports, found)).toEqual({ uri: 'file:///models.py', selection: { startIndex: 30, endIndex: 38 } })
|
||||
expect(await resolveReference('Agent', undefined, imports, found)).toEqual({ uri: 'file:///models.py', selection: { startIndex: 10, endIndex: 15 } })
|
||||
expect(await resolveReference('Agent.owner_id', undefined, imports, found)).toEqual({ uri: 'file:///models.py', selection: { startIndex: 30, endIndex: 38 } })
|
||||
expect(found.asked).toEqual([20, 20])
|
||||
})
|
||||
|
||||
it('导入的是整个模块(定义落在文件开头)时成员从顶层找', async () => {
|
||||
const files = { 'file:///errors.py': [symbol('NotFound', 40, 80)] }
|
||||
const found = sources(files, { 7: { uri: 'file:///errors.py', at: 0, length: 0 } })
|
||||
expect(await resolveReference('errors.NotFound', [{ name: 'errors', offset: 7, range: { startIndex: 7, endIndex: 13 } }], found))
|
||||
expect(await resolveReference('errors.NotFound', undefined, [{ name: 'errors', offset: 7, range: { startIndex: 7, endIndex: 13 } }], found))
|
||||
.toEqual({ uri: 'file:///errors.py', selection: { startIndex: 40, endIndex: 48 } })
|
||||
})
|
||||
|
||||
it('本文件没定义、也没导入,或语言扩展答不上:找不到', async () => {
|
||||
expect(await resolveReference('Nowhere', imports, sources({}))).toBeUndefined()
|
||||
expect(await resolveReference('Agent', imports, sources({}))).toBeUndefined()
|
||||
expect(await resolveReference('Nowhere', undefined, imports, sources({}))).toBeUndefined()
|
||||
expect(await resolveReference('Agent', undefined, imports, sources({}))).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('不带路径的引用:从被注释的声明起一层层往外找(KDoc)', () => {
|
||||
const imports = [{ name: 'Agent', offset: 20, range: { startIndex: 20, endIndex: 25 } }]
|
||||
/** services.py 的样子:AgentService 里套着类 Config 和几个方法,get_default_agent 里有个局部变量;顶层还有个同名的 helper。 */
|
||||
const here = (): ReturnType<typeof sources> => sources({
|
||||
'file:///here.py': [
|
||||
symbol('Agent', 20, 25),
|
||||
symbol('AgentService', 100, 900, [
|
||||
symbol('Config', 120, 180, [symbol('timeout', 140, 150)]),
|
||||
symbol('get_agent_admitted', 200, 300),
|
||||
symbol('get_default_agent', 400, 500, [symbol('agent', 450, 460)]),
|
||||
symbol('helper', 600, 700),
|
||||
]),
|
||||
symbol('helper', 950, 990),
|
||||
],
|
||||
}, { 20: { uri: 'file:///models.py', at: 10, length: 5 } })
|
||||
/** 各个声明的名字在源码里的下标:注释写在它的说明里时从这儿往外找。 */
|
||||
const SERVICE = 100
|
||||
const DEFAULT_AGENT = 400
|
||||
const HELPER = 950
|
||||
const at = (uri: string, start: number, name: string): unknown => ({ uri, selection: { startIndex: start, endIndex: start + name.length } })
|
||||
|
||||
it('方法说明里的 [同类的另一个方法] 找得到', async () => {
|
||||
expect(await resolveReference('get_agent_admitted', DEFAULT_AGENT, imports, here())).toEqual(at('file:///here.py', 200, 'get_agent_admitted'))
|
||||
})
|
||||
|
||||
it('类说明里的 [成员] 找得到;方法说明里的 [自己里面的名字] 也找得到', async () => {
|
||||
expect(await resolveReference('get_default_agent', SERVICE, imports, here())).toEqual(at('file:///here.py', 400, 'get_default_agent'))
|
||||
expect(await resolveReference('agent', DEFAULT_AGENT, imports, here())).toEqual(at('file:///here.py', 450, 'agent'))
|
||||
})
|
||||
|
||||
it('近的先认:方法说明里的 [helper] 是同类的方法,顶层函数说明里的是顶层函数', async () => {
|
||||
expect(await resolveReference('helper', DEFAULT_AGENT, imports, here())).toEqual(at('file:///here.py', 600, 'helper'))
|
||||
expect(await resolveReference('helper', HELPER, imports, here())).toEqual(at('file:///here.py', 950, 'helper'))
|
||||
})
|
||||
|
||||
it('顶层的、导入的照样找得到;导入语句里的名字不算本文件定义的', async () => {
|
||||
const found = here()
|
||||
expect(await resolveReference('AgentService', DEFAULT_AGENT, imports, found)).toEqual(at('file:///here.py', 100, 'AgentService'))
|
||||
expect(await resolveReference('Agent', DEFAULT_AGENT, imports, found)).toEqual(at('file:///models.py', 10, 'Agent'))
|
||||
expect(found.asked).toEqual([20])
|
||||
})
|
||||
|
||||
it('同一个名字写在两块的说明里:方法说明里找得到同类的方法,顶层函数说明里、不在哪个声明的注释里都找不到', async () => {
|
||||
expect(await resolveReference('get_agent_admitted', DEFAULT_AGENT, imports, here())).toEqual(at('file:///here.py', 200, 'get_agent_admitted'))
|
||||
expect(await resolveReference('get_agent_admitted', HELPER, imports, here())).toBeUndefined()
|
||||
expect(await resolveReference('get_agent_admitted', undefined, imports, here())).toBeUndefined()
|
||||
})
|
||||
|
||||
it('写成 Class.member 的:第一段同样一层层找,再往下找成员', async () => {
|
||||
expect(await resolveReference('Config.timeout', DEFAULT_AGENT, imports, here())).toEqual(at('file:///here.py', 140, 'timeout'))
|
||||
expect(await resolveReference('AgentService.get_agent_admitted', HELPER, imports, here())).toEqual(at('file:///here.py', 200, 'get_agent_admitted'))
|
||||
expect(await resolveReference('Config.timeout', HELPER, imports, here())).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('注释写在哪块里:从哪个声明起往外找', () => {
|
||||
function block(anchor: string, defines: number | undefined, children: BlockView[] = []): BlockView {
|
||||
return { anchor, children, ...(defines !== undefined && { defines: { startIndex: defines, endIndex: defines + 1 } }) } as BlockView
|
||||
}
|
||||
const blocks = [
|
||||
block('class AgentService', 100, [
|
||||
block('class AgentService > function get_default_agent', 400, [
|
||||
block('class AgentService > function get_default_agent > if @3fa1c2d0', undefined),
|
||||
]),
|
||||
]),
|
||||
block('comment @0b1c2d3e', undefined),
|
||||
]
|
||||
|
||||
it('有名字的块(声明)是它自己的名字;没名字的(注释、语句)取外层最近的有名字的块', () => {
|
||||
expect(referenceScope(blocks, 'class AgentService')).toBe(100)
|
||||
expect(referenceScope(blocks, 'class AgentService > function get_default_agent')).toBe(400)
|
||||
expect(referenceScope(blocks, 'class AgentService > function get_default_agent > if @3fa1c2d0')).toBe(400)
|
||||
})
|
||||
|
||||
it('顶层没名字的块、块树里没有的块:没有,只从文件顶层找', () => {
|
||||
expect(referenceScope(blocks, 'comment @0b1c2d3e')).toBeUndefined()
|
||||
expect(referenceScope(blocks, 'class Gone')).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in new issue
Block a user