Skip to content

Commit beba952

Browse files
committed
fix: hardly cache API responses, now we make as few requests as possible to TS server. eg now thousands of extensions can use api in completion providers without affecting performance
fix: also allow to pass document and position (offset alternative) in API commands options as second arg
1 parent 67da634 commit beba952

File tree

2 files changed

+42
-5
lines changed

2 files changed

+42
-5
lines changed

src/apiCommands.ts

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,53 @@ import { passthroughExposedApiCommands, TriggerCharacterCommand } from '../types
44
import { sendCommand } from './sendCommand'
55

66
export default () => {
7-
const sharedRequest = (type: TriggerCharacterCommand, { offset, relativeOffset = 0 }: RequestOptions) => {
7+
/** @unique */
8+
const cacheableCommands: Set<typeof passthroughExposedApiCommands[number]> = new Set(['getNodePath', 'getSpanOfEnclosingComment', 'getNodeAtPosition'])
9+
const operationsCache = new Map<string, { key: string; data }>()
10+
const sharedRequest = async (type: TriggerCharacterCommand, { offset, relativeOffset = 0, document, position }: RequestOptions) => {
11+
if (position && offset) throw new Error('Only position or offset parameter can be provided')
12+
if (document && !offset && !position) throw new Error('When custom document is provided, offset or position must be provided')
13+
814
const { activeTextEditor } = vscode.window
9-
if (!activeTextEditor) return
10-
const { document, selection } = activeTextEditor
11-
offset ??= document.offsetAt(selection.active) + relativeOffset
12-
return sendCommand(type, { document, position: document.positionAt(offset) })
15+
document ??= activeTextEditor?.document
16+
if (!document) return
17+
if (!position) offset ??= document.offsetAt(activeTextEditor!.selection.active) + relativeOffset
18+
const requestOffset = offset ?? document.offsetAt(position!)
19+
const requestPos = position ?? document.positionAt(offset!)
20+
const getData = async () => sendCommand(type, { document: document!, position: requestPos })
21+
if (cacheableCommands.has(type as any)) {
22+
const cacheEntry = operationsCache.get(type)
23+
const operationKey = `${document.uri.toString()}:${document.version}:${requestOffset}`
24+
if (cacheEntry?.key === operationKey) {
25+
return cacheEntry.data
26+
}
27+
28+
const data = getData()
29+
// intentionally storing data only per one offset because it was created for this specific case:
30+
// extension 1 completion provider requests API data
31+
// at the same time:
32+
// extension 2 completion provider requests API data at the same document and position
33+
// and so on
34+
operationsCache.set(type, { key: operationKey, data })
35+
if (type === 'getNodePath') {
36+
operationsCache.set('getNodeAtPosition', { key: operationKey, data: data.then((path: any) => path[path.length - 1]) })
37+
}
38+
39+
return data
40+
}
41+
42+
return getData()
1343
}
1444

1545
type RequestOptions = Partial<{
46+
/**
47+
* Should be rarely overrided, this document must be part of opened project
48+
* If specificed, offset or position must be provided too
49+
*/
50+
document: vscode.TextDocument
1651
offset: number
1752
relativeOffset: number
53+
position: vscode.Position
1854
}>
1955
for (const cmd of passthroughExposedApiCommands)
2056
vscode.commands.registerCommand(getExtensionCommandId(cmd as never), async (options: RequestOptions = {}) => sharedRequest(cmd, options))

typescript/src/specialCommands/handle.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export default (
4646
}
4747
}
4848
if (specialCommand === 'getNodeAtPosition') {
49+
// ensure return data is the same as for node in getNodePath
4950
const node = findChildContainingPosition(ts, sourceFile, position)
5051
return {
5152
entries: [],

0 commit comments

Comments
 (0)