From 6e2c541558207245ed4bd87e1329ed1773ed002c Mon Sep 17 00:00:00 2001 From: cruldra Date: Tue, 13 Oct 2026 14:40:07 +1100 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20perf(vscode):=20=E6=9F=A5=E5=88=B0?= =?UTF-8?q?=E7=9A=84=E5=AE=9A=E4=B9=89=E8=90=BD=E5=88=B0=E5=90=8D=E5=AD=97?= =?UTF-8?q?=E4=B8=8A=E6=97=B6=E5=8F=AA=E8=AF=BB=E5=AE=9A=E4=B9=89=E9=82=A3?= =?UTF-8?q?=E4=B8=80=E6=AE=B5=EF=BC=8C=E4=B8=8D=E6=8A=8A=E7=9B=AE=E6=A0=87?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=95=B4=E4=BB=BD=E5=8F=96=E5=87=BA=E6=9D=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/codeReader/editor/definitions.test.ts | 43 ++++++++++++++++++- vscode/src/codeReader/editor/definitions.ts | 14 ++++-- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/vscode/src/codeReader/editor/definitions.test.ts b/vscode/src/codeReader/editor/definitions.test.ts index 57fbc29..a032c5c 100644 --- a/vscode/src/codeReader/editor/definitions.test.ts +++ b/vscode/src/codeReader/editor/definitions.test.ts @@ -1,10 +1,13 @@ import { describe, expect, it, vi } from 'vitest' -import { definitionOffset } from './definitions' +import { commands, workspace } from 'vscode' +import { definitionOffset, languageDefinitions } from './definitions' vi.mock('vscode', () => ({ commands: { executeCommand: vi.fn() }, EventEmitter: class EventEmitter { event = vi.fn(); fire = vi.fn() }, - Range: class Range {}, + Range: class Range { + constructor(readonly start: { offset: number }, readonly end: { offset: number }) {} + }, Uri: { parse: vi.fn() }, window: { createOutputChannel: vi.fn(() => ({ appendLine: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() })) }, workspace: { openTextDocument: vi.fn(), asRelativePath: vi.fn() }, @@ -40,3 +43,39 @@ describe('定义范围的起点不在名字上时,用范围里那个名字', ( expect(definitionOffset(text, { start: 0, end: text.length }, undefined, 'fetchAppDeployments')).toBe(text.lastIndexOf('fetchAppDeployments')) }) }) + +/** 假的文档:位置就是下标;记下 getText 每次取了多长。 */ +function fakeDocument(uri: string, text: string): { reads: number[] } { + const reads: number[] = [] + const at = (offset: number) => ({ offset }) + const document = { + uri: { toString: () => uri }, + offsetAt: (position: { offset: number }) => position.offset, + positionAt: (offset: number) => at(Math.max(0, Math.min(offset, text.length))), + getText: (range?: { start: { offset: number }, end: { offset: number } }) => { + const read = range ? text.slice(range.start.offset, range.end.offset) : text + reads.push(read.length) + return read + }, + getWordRangeAtPosition: (position: { offset: number }) => { + const start = text.slice(0, position.offset).search(/\w+$/) + const end = position.offset + (/^\w*/.exec(text.slice(position.offset))?.[0].length ?? 0) + return { start: at(start < 0 ? position.offset : start), end: at(end) } + }, + } + vi.mocked(workspace.openTextDocument).mockImplementation((async (target: { toString: () => string }) => target.toString() === uri ? document : undefined) as never) + return { reads } +} + +describe('查到的定义落到名字上', () => { + it('只读定义那一段(前后各多一个字认名字的边界),不把目标文件整份取出来', async () => { + const text = `${'# 很长的文件\n'.repeat(2000)}def fetch_app(x):\n return fetch_app(x)\n` + const { reads } = fakeDocument('file:///w/a.py', text) + const call = text.lastIndexOf('fetch_app') + const declaration = text.indexOf('def fetch_app') + vi.mocked(commands.executeCommand).mockResolvedValue([{ uri: { toString: () => 'file:///w/a.py' }, range: { start: { offset: declaration }, end: { offset: text.indexOf(':', declaration) } } }] as never) + const [found] = await languageDefinitions({ toString: () => 'file:///w/a.py' } as never, { offset: call } as never) + expect((found.range.start as unknown as { offset: number }).offset).toBe(text.indexOf('fetch_app', declaration)) + expect(Math.max(...reads)).toBeLessThan(40) + }) +}) diff --git a/vscode/src/codeReader/editor/definitions.ts b/vscode/src/codeReader/editor/definitions.ts index 5f7a562..1224ec8 100644 --- a/vscode/src/codeReader/editor/definitions.ts +++ b/vscode/src/codeReader/editor/definitions.ts @@ -108,16 +108,22 @@ async function rangeOnName(uri: Uri, range: Range, selection: Range | undefined, return fallback try { const document = await workspace.openTextDocument(uri) - const text = document.getText() + const start = document.offsetAt(range.start) + const end = document.offsetAt(range.end) + const selected = selection && { start: document.offsetAt(selection.start), end: document.offsetAt(selection.end) } + // 只取定义这一段,前后各多一个字(认名字的边界):目标文件可能很大,每处定义都整份取一遍太费 + const from = Math.max(0, Math.min(start, selected?.start ?? start) - 1) + const to = Math.max(end, (selected?.start ?? start) + name.length) + 1 + const text = document.getText(new Range(document.positionAt(from), document.positionAt(to))) const offset = definitionOffset( text, - { start: document.offsetAt(range.start), end: document.offsetAt(range.end) }, - selection ? { start: document.offsetAt(selection.start), end: document.offsetAt(selection.end) } : undefined, + { start: start - from, end: end - from }, + selected && { start: selected.start - from, end: selected.end - from }, name, ) if (!onName(text, offset, name)) return fallback - return new Range(document.positionAt(offset), document.positionAt(offset + name.length)) + return new Range(document.positionAt(from + offset), document.positionAt(from + offset + name.length)) } catch (err) { logger.add({ level: 'warn', source: 'codeReader', message: `读不到定义所在的 ${uri.toString()}:${err instanceof Error ? err.message : String(err)}` })