Skip to content

Commit 22f51ab

Browse files
jeanp413eamodio
authored andcommitted
Fixes #3571 Properly set uri context keys
1 parent 0d99fbc commit 22f51ab

File tree

5 files changed

+202
-68
lines changed

5 files changed

+202
-68
lines changed

src/annotations/fileAnnotationController.ts

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ import { registerCommand } from '../system/vscode/command';
3333
import { configuration } from '../system/vscode/configuration';
3434
import { setContext } from '../system/vscode/context';
3535
import type { KeyboardScope } from '../system/vscode/keyboard';
36-
import { getResourceContextKeyValue, isTrackableTextEditor } from '../system/vscode/utils';
36+
import { ResourceSet } from '../system/vscode/map';
37+
import { isTrackableTextEditor } from '../system/vscode/utils';
3738
import type {
3839
DocumentBlameStateChangeEvent,
3940
DocumentDirtyIdleTriggerEvent,
@@ -327,8 +328,8 @@ export class FileAnnotationController implements Disposable {
327328
debouncedRestore(editor);
328329
}
329330

330-
private readonly _annotatedUris = new Set<string>();
331-
private readonly _computingUris = new Set<string>();
331+
private readonly _annotatedUris = new ResourceSet();
332+
private readonly _computingUris = new ResourceSet();
332333

333334
async onProviderEditorStatusChanged(editor: TextEditor | undefined, status: AnnotationStatus | undefined) {
334335
if (editor == null) return;
@@ -345,50 +346,46 @@ export class FileAnnotationController implements Disposable {
345346
} else {
346347
windowStatus = undefined;
347348

348-
let key = getResourceContextKeyValue(editor.document.uri);
349-
if (typeof key !== 'string') {
350-
key = await key;
351-
}
352-
349+
const uri = editor.document.uri;
353350
switch (status) {
354351
case 'computing':
355-
if (!this._annotatedUris.has(key)) {
356-
this._annotatedUris.add(key);
352+
if (!this._annotatedUris.has(uri)) {
353+
this._annotatedUris.add(uri);
357354
changed = true;
358355
}
359356

360-
if (!this._computingUris.has(key)) {
361-
this._computingUris.add(key);
357+
if (!this._computingUris.has(uri)) {
358+
this._computingUris.add(uri);
362359
changed = true;
363360
}
364361

365362
break;
366363
case 'computed': {
367364
const provider = this.getProvider(editor);
368365
if (provider == null) {
369-
if (this._annotatedUris.has(key)) {
370-
this._annotatedUris.delete(key);
366+
if (this._annotatedUris.has(uri)) {
367+
this._annotatedUris.delete(uri);
371368
changed = true;
372369
}
373-
} else if (!this._annotatedUris.has(key)) {
374-
this._annotatedUris.add(key);
370+
} else if (!this._annotatedUris.has(uri)) {
371+
this._annotatedUris.add(uri);
375372
changed = true;
376373
}
377374

378-
if (this._computingUris.has(key)) {
379-
this._computingUris.delete(key);
375+
if (this._computingUris.has(uri)) {
376+
this._computingUris.delete(uri);
380377
changed = true;
381378
}
382379
break;
383380
}
384381
default:
385-
if (this._annotatedUris.has(key)) {
386-
this._annotatedUris.delete(key);
382+
if (this._annotatedUris.has(uri)) {
383+
this._annotatedUris.delete(uri);
387384
changed = true;
388385
}
389386

390-
if (this._computingUris.has(key)) {
391-
this._computingUris.delete(key);
387+
if (this._computingUris.has(uri)) {
388+
this._computingUris.delete(uri);
392389
changed = true;
393390
}
394391
break;

src/constants.context.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { Uri } from 'vscode';
12
import type { AnnotationStatus } from './annotations/annotationProvider';
23
import type { Keys, PromoKeys } from './constants';
34
import type { CustomEditorTypes, WebviewTypes, WebviewViewTypes } from './constants.views';
@@ -26,10 +27,10 @@ export type ContextKeys = {
2627
'gitlens:repos:withHostingIntegrations': string[];
2728
'gitlens:repos:withHostingIntegrationsConnected': string[];
2829
'gitlens:schemes:trackable': string[];
29-
'gitlens:tabs:annotated': string[];
30-
'gitlens:tabs:annotated:computing': string[];
31-
'gitlens:tabs:blameable': string[];
32-
'gitlens:tabs:tracked': string[];
30+
'gitlens:tabs:annotated': Uri[];
31+
'gitlens:tabs:annotated:computing': Uri[];
32+
'gitlens:tabs:blameable': Uri[];
33+
'gitlens:tabs:tracked': Uri[];
3334
'gitlens:untrusted': boolean;
3435
'gitlens:views:canCompare': boolean;
3536
'gitlens:views:canCompare:file': boolean;

src/system/vscode/map.ts

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import type { Uri } from 'vscode';
2+
3+
interface ResourceMapKeyFn {
4+
(resource: Uri): string;
5+
}
6+
7+
class ResourceMapEntry<T> {
8+
constructor(readonly uri: Uri, readonly value: T) { }
9+
}
10+
11+
function isEntries<T>(arg: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [Uri, T])[] | undefined): arg is readonly (readonly [Uri, T])[] {
12+
return Array.isArray(arg);
13+
}
14+
15+
export class ResourceMap<T> implements Map<Uri, T> {
16+
17+
private static readonly defaultToKey = (resource: Uri) => resource.toString();
18+
19+
readonly [Symbol.toStringTag] = 'ResourceMap';
20+
21+
private readonly map: Map<string, ResourceMapEntry<T>>;
22+
private readonly toKey: ResourceMapKeyFn;
23+
24+
constructor(toKey?: ResourceMapKeyFn);
25+
26+
constructor(other?: ResourceMap<T>, toKey?: ResourceMapKeyFn);
27+
28+
constructor(entries?: readonly (readonly [Uri, T])[], toKey?: ResourceMapKeyFn);
29+
30+
constructor(arg?: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [Uri, T])[], toKey?: ResourceMapKeyFn) {
31+
if (arg instanceof ResourceMap) {
32+
this.map = new Map(arg.map);
33+
this.toKey = toKey ?? ResourceMap.defaultToKey;
34+
} else if (isEntries(arg)) {
35+
this.map = new Map();
36+
this.toKey = toKey ?? ResourceMap.defaultToKey;
37+
38+
for (const [resource, value] of arg) {
39+
this.set(resource, value);
40+
}
41+
} else {
42+
this.map = new Map();
43+
this.toKey = arg ?? ResourceMap.defaultToKey;
44+
}
45+
}
46+
47+
set(resource: Uri, value: T): this {
48+
this.map.set(this.toKey(resource), new ResourceMapEntry(resource, value));
49+
return this;
50+
}
51+
52+
get(resource: Uri): T | undefined {
53+
return this.map.get(this.toKey(resource))?.value;
54+
}
55+
56+
has(resource: Uri): boolean {
57+
return this.map.has(this.toKey(resource));
58+
}
59+
60+
get size(): number {
61+
return this.map.size;
62+
}
63+
64+
clear(): void {
65+
this.map.clear();
66+
}
67+
68+
delete(resource: Uri): boolean {
69+
return this.map.delete(this.toKey(resource));
70+
}
71+
72+
forEach(clb: (value: T, key: Uri, map: Map<Uri, T>) => void, thisArg?: any): void {
73+
if (typeof thisArg !== 'undefined') {
74+
clb = clb.bind(thisArg);
75+
}
76+
for (const [_, entry] of this.map) {
77+
clb(entry.value, entry.uri, (this as any));
78+
}
79+
}
80+
81+
*values(): MapIterator<T> {
82+
for (const entry of this.map.values()) {
83+
yield entry.value;
84+
}
85+
}
86+
87+
*keys(): MapIterator<Uri> {
88+
for (const entry of this.map.values()) {
89+
yield entry.uri;
90+
}
91+
}
92+
93+
*entries(): MapIterator<[Uri, T]> {
94+
for (const entry of this.map.values()) {
95+
yield [entry.uri, entry.value];
96+
}
97+
}
98+
99+
*[Symbol.iterator](): MapIterator<[Uri, T]> {
100+
for (const [, entry] of this.map) {
101+
yield [entry.uri, entry.value];
102+
}
103+
}
104+
}
105+
106+
export class ResourceSet implements Set<Uri> {
107+
108+
readonly [Symbol.toStringTag]: string = 'ResourceSet';
109+
110+
private readonly _map: ResourceMap<Uri>;
111+
112+
constructor(toKey?: ResourceMapKeyFn);
113+
constructor(entries: readonly Uri[], toKey?: ResourceMapKeyFn);
114+
constructor(entriesOrKey?: readonly Uri[] | ResourceMapKeyFn, toKey?: ResourceMapKeyFn) {
115+
if (!entriesOrKey || typeof entriesOrKey === 'function') {
116+
this._map = new ResourceMap(entriesOrKey);
117+
} else {
118+
this._map = new ResourceMap(toKey);
119+
entriesOrKey.forEach(this.add, this);
120+
}
121+
}
122+
123+
124+
get size(): number {
125+
return this._map.size;
126+
}
127+
128+
add(value: Uri): this {
129+
this._map.set(value, value);
130+
return this;
131+
}
132+
133+
clear(): void {
134+
this._map.clear();
135+
}
136+
137+
delete(value: Uri): boolean {
138+
return this._map.delete(value);
139+
}
140+
141+
forEach(callbackfn: (value: Uri, value2: Uri, set: Set<Uri>) => void, thisArg?: any): void {
142+
this._map.forEach((_value, key) => callbackfn.call(thisArg, key, key, this));
143+
}
144+
145+
has(value: Uri): boolean {
146+
return this._map.has(value);
147+
}
148+
149+
entries(): SetIterator<[Uri, Uri]> {
150+
return this._map.entries();
151+
}
152+
153+
keys(): SetIterator<Uri> {
154+
return this._map.keys();
155+
}
156+
157+
values(): SetIterator<Uri> {
158+
return this._map.keys();
159+
}
160+
161+
[Symbol.iterator](): SetIterator<Uri> {
162+
return this.keys();
163+
}
164+
}

src/system/vscode/utils.ts

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -352,26 +352,3 @@ export function tabContainsUri(tab: Tab | undefined, uri: Uri | undefined): bool
352352

353353
return false;
354354
}
355-
356-
const resourceContextKeyValueCache = new Map<string, Thenable<string>>();
357-
358-
export function getResourceContextKeyValue(uri: Uri) {
359-
// If we are on a remote connection, VS Code's `TextDocument.uri` uses a `file://` scheme,
360-
// but VS Code sets the `resource` context key to a "remote" url in the form of `vscode-remote://<remote-type>+<remote-host?>/<path>`
361-
// So we need to try to generate that `vscode-remote://` version, which seems to work by getting the querystring from `env.asExternalUri`
362-
if (uri.scheme === 'file' && env.remoteName) {
363-
const uriKey = uri.toString();
364-
let promise = resourceContextKeyValueCache.get(uriKey);
365-
if (promise == null) {
366-
promise = env.asExternalUri(uri).then(u => u.query);
367-
resourceContextKeyValueCache.set(uriKey, promise);
368-
promise.then(
369-
() => resourceContextKeyValueCache.delete(uriKey),
370-
() => resourceContextKeyValueCache.delete(uriKey),
371-
);
372-
}
373-
return promise;
374-
}
375-
376-
return uri.toString();
377-
}

src/trackers/documentTracker.ts

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import type { Deferrable } from '../system/function';
2222
import { debounce } from '../system/function';
2323
import { configuration } from '../system/vscode/configuration';
2424
import { setContext } from '../system/vscode/context';
25-
import { findTextDocument, getResourceContextKeyValue, isVisibleDocument } from '../system/vscode/utils';
25+
import { ResourceSet } from '../system/vscode/map';
26+
import { findTextDocument, isVisibleDocument } from '../system/vscode/utils';
2627
import type { TrackedGitDocument } from './trackedDocument';
2728
import { createTrackedGitDocument } from './trackedDocument';
2829

@@ -414,33 +415,33 @@ export class GitDocumentTracker implements Disposable {
414415
(tracked ?? (await docPromise))?.dispose();
415416
}
416417

417-
private readonly _openUrisBlameable = new Set<string>();
418-
private readonly _openUrisTracked = new Set<string>();
418+
private readonly _openUrisBlameable = new ResourceSet();
419+
private readonly _openUrisTracked = new ResourceSet();
419420
private _updateContextDebounced: Deferrable<() => void> | undefined;
420421

421422
updateContext(uri: Uri, blameable: boolean, tracked: boolean) {
422423
let changed = false;
423424

424-
function updateContextCore(this: GitDocumentTracker, key: string, blameable: boolean, tracked: boolean) {
425+
function updateContextCore(this: GitDocumentTracker, uri: Uri, blameable: boolean, tracked: boolean) {
425426
if (tracked) {
426-
if (!this._openUrisTracked.has(key)) {
427+
if (!this._openUrisTracked.has(uri)) {
427428
changed = true;
428-
this._openUrisTracked.add(key);
429+
this._openUrisTracked.add(uri);
429430
}
430-
} else if (this._openUrisTracked.has(key)) {
431+
} else if (this._openUrisTracked.has(uri)) {
431432
changed = true;
432-
this._openUrisTracked.delete(key);
433+
this._openUrisTracked.delete(uri);
433434
}
434435

435436
if (blameable) {
436-
if (!this._openUrisBlameable.has(key)) {
437+
if (!this._openUrisBlameable.has(uri)) {
437438
changed = true;
438439

439-
this._openUrisBlameable.add(key);
440+
this._openUrisBlameable.add(uri);
440441
}
441-
} else if (this._openUrisBlameable.has(key)) {
442+
} else if (this._openUrisBlameable.has(uri)) {
442443
changed = true;
443-
this._openUrisBlameable.delete(key);
444+
this._openUrisBlameable.delete(uri);
444445
}
445446

446447
if (!changed) return;
@@ -452,13 +453,7 @@ export class GitDocumentTracker implements Disposable {
452453
this._updateContextDebounced();
453454
}
454455

455-
const key = getResourceContextKeyValue(uri);
456-
if (typeof key !== 'string') {
457-
void key.then(u => updateContextCore.call(this, u, blameable, tracked));
458-
return;
459-
}
460-
461-
updateContextCore.call(this, key, blameable, tracked);
456+
updateContextCore.call(this, uri, blameable, tracked);
462457
}
463458

464459
private fireDocumentDirtyStateChanged(e: DocumentDirtyStateChangeEvent) {

0 commit comments

Comments
 (0)