@trim21/personal-pi-extensions 0.0.355 → 0.0.357

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trim21/personal-pi-extensions",
3
- "version": "0.0.355",
3
+ "version": "0.0.357",
4
4
  "type": "module",
5
5
  "description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
6
6
  "keywords": [
@@ -210,6 +210,8 @@ export async function create(input: CreateInput): Promise<LspClient> {
210
210
  const diagnosticRegistrations = new Map<string, CapabilityRegistration>();
211
211
  const registrationListeners = new Set<() => void>();
212
212
  const diagnosticListeners = new Set<(input: { path: string; serverID: string }) => void>();
213
+ /** resolvedPath → 客户端已发送的最新文档版本(didOpen=0,didChange 递增)。 */
214
+ const documentVersions = new Map<string, number>();
213
215
  const mergedDiagnostics = (filePath: string): Diagnostic[] =>
214
216
  dedupeDiagnostics([
215
217
  ...(pushDiagnostics.get(filePath) ?? []),
@@ -234,6 +236,17 @@ export async function create(input: CreateInput): Promise<LspClient> {
234
236
  (params: { uri: string; version?: number; diagnostics: Diagnostic[] }) => {
235
237
  const filePath = getFilePath(params.uri);
236
238
  if (!filePath) return;
239
+ // 支持 pull 诊断的服务器:push 的结果可能来自旧内容(异步重算迟到),
240
+ // 直接忽略,只信任 pull 返回的当前文档结果,从源头避免 stale。
241
+ if (supportsPullDiagnostics()) return;
242
+ // 纯 push 服务器:服务器版本滞后于已发送版本时,该 push 对应的是旧内容
243
+ // (重算未完成时的迟到结果),同样忽略。
244
+ const currentVersion = documentVersions.get(filePath);
245
+ const isStalePush =
246
+ typeof params.version === "number" &&
247
+ currentVersion !== undefined &&
248
+ params.version !== currentVersion;
249
+ if (isStalePush) return;
237
250
  published.set(filePath, {
238
251
  at: Date.now(),
239
252
  version: typeof params.version === "number" ? params.version : undefined,
@@ -406,6 +419,15 @@ export async function create(input: CreateInput): Promise<LspClient> {
406
419
  return { handled: true, matched, byFile };
407
420
  }
408
421
 
422
+ /** 是否支持文档级 pull 诊断:静态 diagnosticProvider 或动态注册的 document 诊断。 */
423
+ function supportsPullDiagnostics(): boolean {
424
+ if (hasStaticPullDiagnostics) return true;
425
+ for (const registration of diagnosticRegistrations.values()) {
426
+ if (registration.registerOptions?.workspaceDiagnostics !== true) return true;
427
+ }
428
+ return false;
429
+ }
430
+
409
431
  function documentPullState() {
410
432
  const documentRegistrations = [...diagnosticRegistrations.values()].filter(
411
433
  (registration) => registration.registerOptions?.workspaceDiagnostics !== true,
@@ -414,7 +436,7 @@ export async function create(input: CreateInput): Promise<LspClient> {
414
436
  documentIdentifiers: [
415
437
  ...new Set(documentRegistrations.flatMap((r) => r.registerOptions?.identifier ?? [])),
416
438
  ],
417
- supported: hasStaticPullDiagnostics || documentRegistrations.length > 0,
439
+ supported: supportsPullDiagnostics(),
418
440
  };
419
441
  }
420
442
 
@@ -579,6 +601,9 @@ export async function create(input: CreateInput): Promise<LspClient> {
579
601
  while (Date.now() - startedAt < diagnosticsDocumentWaitTimeoutMs) {
580
602
  const result = await requestDocumentDiagnostics(request.path);
581
603
  if (result.matched) return;
604
+ // 支持 pull 的服务器:pull 未匹配(失败/超时)即返回,不依赖 push 兜底
605
+ // (push 已被忽略);纯 push 服务器继续走下面的 push 等待。
606
+ if (supportsPullDiagnostics()) return;
582
607
  const remaining = diagnosticsDocumentWaitTimeoutMs - (Date.now() - startedAt);
583
608
  if (remaining <= 0) return;
584
609
  const next = await Promise.race([
@@ -607,6 +632,7 @@ export async function create(input: CreateInput): Promise<LspClient> {
607
632
  while (Date.now() - startedAt < diagnosticsFullWaitTimeoutMs) {
608
633
  const result = await requestFullDiagnostics(request.path);
609
634
  if (result.handled || result.matched) return;
635
+ if (supportsPullDiagnostics()) return;
610
636
  const remaining = diagnosticsFullWaitTimeoutMs - (Date.now() - startedAt);
611
637
  if (remaining <= 0) return;
612
638
  const next = await Promise.race([
@@ -652,6 +678,7 @@ export async function create(input: CreateInput): Promise<LspClient> {
652
678
 
653
679
  const next = document.version + 1;
654
680
  files[resolvedPath] = { version: next, text };
681
+ documentVersions.set(resolvedPath, next);
655
682
  await connection.sendNotification("textDocument/didChange", {
656
683
  textDocument: { uri, version: next },
657
684
  contentChanges:
@@ -677,6 +704,7 @@ export async function create(input: CreateInput): Promise<LspClient> {
677
704
  textDocument: { uri, languageId, version: 0, text },
678
705
  });
679
706
  files[resolvedPath] = { version: 0, text };
707
+ documentVersions.set(resolvedPath, 0);
680
708
  return 0;
681
709
  },
682
710
  },