⚡ perf(vscode): 预览发文档后在后台问语言扩展的请求排队发:同时在途最多 4 个、离视口近的先问、文档改了排着的作废,看得见的中文名先交
This commit is contained in:
1 parent
5a0c5490ea
commit
12ad047b4d
7 files changed
+335
-45
No files matched your search
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* 同时在途的最多几个。语言服务一次只处理一个请求(Pylance、tsserver 都是;VS Code 自带的 TypeScript 扩展也在自己这边排队,
|
||||
* 一次只往 tsserver 发一个),多发出去的只是在它那儿排着,还把用户自己的悬停、跳到定义挤到后面;留几个在途够填上来回的空当。
|
||||
*/
|
||||
const LIMIT = 4
|
||||
|
||||
/** 页面只报视口顶部那块的起始行,一屏按这么多行算。 */
|
||||
const SCREEN_LINES = 60
|
||||
|
||||
/** 排着的一个请求。 */
|
||||
interface Waiting {
|
||||
version: number
|
||||
line: number
|
||||
start: () => void
|
||||
drop: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
* 文档发出去之后在后台问语言扩展的请求(中文名、库规则查的定义、调用的种类,连同没答时隔一会儿的重试)排队发:
|
||||
* 同时在途的最多 limit 个;每空出一个位子挑排着的里面离视口最近的(视口顶部往下的按远近,往上的排在往下一屏之后);
|
||||
* 文档换了版本,排着的旧版本请求不再发,按没答交回去(问的那边见版本变了会丢掉)。用户自己点的(悬停、跳到定义)不排。这里不碰 VS Code。
|
||||
*/
|
||||
export class LanguageRequests {
|
||||
private waiting: Waiting[] = []
|
||||
private running = 0
|
||||
|
||||
constructor(
|
||||
/** 文档眼下的版本 */
|
||||
private readonly version: () => number,
|
||||
/** 页面视口顶部那块的起始行;页面还没报时没有,按文件开头算 */
|
||||
private readonly visibleLine: () => number | undefined,
|
||||
private readonly limit = LIMIT,
|
||||
) {}
|
||||
|
||||
/** 排一个 version 这一版文档、问第 line 行的请求;没发就作废的交回 dropped。 */
|
||||
run<T>(version: number, line: number, request: () => Promise<T>, dropped: T): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
this.waiting.push({
|
||||
version,
|
||||
line,
|
||||
start: () => {
|
||||
request().then(resolve, reject).finally(() => {
|
||||
this.running--
|
||||
this.next()
|
||||
})
|
||||
},
|
||||
drop: () => resolve(dropped),
|
||||
})
|
||||
this.next()
|
||||
})
|
||||
}
|
||||
|
||||
/** 第 line 行在不在视口里:视口顶部往下一屏。 */
|
||||
visible(line: number): boolean {
|
||||
const top = this.visibleLine() ?? 0
|
||||
return line >= top && line < top + SCREEN_LINES
|
||||
}
|
||||
|
||||
/** 第 line 行离视口多远:视口顶部往下的按行数,往上的排在往下一屏之后。 */
|
||||
distance(line: number): number {
|
||||
const top = this.visibleLine() ?? 0
|
||||
return line >= top ? line - top : top - line + SCREEN_LINES
|
||||
}
|
||||
|
||||
/** 预览关了:排着的都作废。 */
|
||||
dispose(): void {
|
||||
const waiting = this.waiting
|
||||
this.waiting = []
|
||||
for (const item of waiting)
|
||||
item.drop()
|
||||
}
|
||||
|
||||
private next(): void {
|
||||
const version = this.version()
|
||||
const stale = this.waiting.filter(item => item.version !== version)
|
||||
if (stale.length > 0) {
|
||||
this.waiting = this.waiting.filter(item => item.version === version)
|
||||
for (const item of stale)
|
||||
item.drop()
|
||||
}
|
||||
while (this.running < this.limit && this.waiting.length > 0) {
|
||||
let nearest = 0
|
||||
let nearestDistance = this.distance(this.waiting[0].line)
|
||||
for (let i = 1; i < this.waiting.length; i++) {
|
||||
const distance = this.distance(this.waiting[i].line)
|
||||
if (distance < nearestDistance) {
|
||||
nearest = i
|
||||
nearestDistance = distance
|
||||
}
|
||||
}
|
||||
const [item] = this.waiting.splice(nearest, 1)
|
||||
this.running++
|
||||
item.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user