Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Fixed

- Fixes [#3592](https://github.com/gitkraken/vscode-gitlens/issues/3592) - Connecting to an integration via Remotes view (but likely others) doesn't work
- Fixes [#3571](https://github.com/gitkraken/vscode-gitlens/issues/3571) - Gitlens fails to register buttons on top-right corner — thanks to [PR #3605](https://github.com/gitkraken/vscode-gitlens/pull/3605) by Jean Pierre ([@jeanp413](https://github.com/jeanp413))
- Fixes an issue where virtual repositories for GitHub PRs from forks wouldn't load properly
- Fixes an issue where deleting a worktree would not always remove the worktree from the view

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ A big thanks to the people that have contributed to this project 🙏❤️:
- may ([@m4rch3n1ng](https://github.com/m4rch3n1ng)) — [contributions](https://github.com/gitkraken/vscode-gitlens/commits?author=m4rch3n1ng)
- bm-w ([@bm-w](https://github.com/bm-w)) — [contributions](https://github.com/gitkraken/vscode-gitlens/commits?author=bm-w)
- Tyler Johnson ([@TJohnsonSE](https://github.com/TJohnsonSE)) — [contributions](https://github.com/gitkraken/vscode-gitlens/commits?author=TJohnsonSE)
- Jean Pierre ([@jeanp413](https://github.com/jeanp413)) — [contributions](https://github.com/gitkraken/vscode-gitlens/commits?author=jeanp413)

Also special thanks to the people that have provided support, testing, brainstorming, etc:

Expand Down
41 changes: 19 additions & 22 deletions src/annotations/fileAnnotationController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ import { registerCommand } from '../system/vscode/command';
import { configuration } from '../system/vscode/configuration';
import { setContext } from '../system/vscode/context';
import type { KeyboardScope } from '../system/vscode/keyboard';
import { getResourceContextKeyValue, isTrackableTextEditor } from '../system/vscode/utils';
import { UriSet } from '../system/vscode/uriMap';
import { isTrackableTextEditor } from '../system/vscode/utils';
import type {
DocumentBlameStateChangeEvent,
DocumentDirtyIdleTriggerEvent,
Expand Down Expand Up @@ -327,8 +328,8 @@ export class FileAnnotationController implements Disposable {
debouncedRestore(editor);
}

private readonly _annotatedUris = new Set<string>();
private readonly _computingUris = new Set<string>();
private readonly _annotatedUris = new UriSet();
private readonly _computingUris = new UriSet();

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

let key = getResourceContextKeyValue(editor.document.uri);
if (typeof key !== 'string') {
key = await key;
}

const uri = editor.document.uri;
switch (status) {
case 'computing':
if (!this._annotatedUris.has(key)) {
this._annotatedUris.add(key);
if (!this._annotatedUris.has(uri)) {
this._annotatedUris.add(uri);
changed = true;
}

if (!this._computingUris.has(key)) {
this._computingUris.add(key);
if (!this._computingUris.has(uri)) {
this._computingUris.add(uri);
changed = true;
}

break;
case 'computed': {
const provider = this.getProvider(editor);
if (provider == null) {
if (this._annotatedUris.has(key)) {
this._annotatedUris.delete(key);
if (this._annotatedUris.has(uri)) {
this._annotatedUris.delete(uri);
changed = true;
}
} else if (!this._annotatedUris.has(key)) {
this._annotatedUris.add(key);
} else if (!this._annotatedUris.has(uri)) {
this._annotatedUris.add(uri);
changed = true;
}

if (this._computingUris.has(key)) {
this._computingUris.delete(key);
if (this._computingUris.has(uri)) {
this._computingUris.delete(uri);
changed = true;
}
break;
}
default:
if (this._annotatedUris.has(key)) {
this._annotatedUris.delete(key);
if (this._annotatedUris.has(uri)) {
this._annotatedUris.delete(uri);
changed = true;
}

if (this._computingUris.has(key)) {
this._computingUris.delete(key);
if (this._computingUris.has(uri)) {
this._computingUris.delete(uri);
changed = true;
}
break;
Expand Down
9 changes: 5 additions & 4 deletions src/constants.context.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Uri } from 'vscode';
import type { AnnotationStatus } from './annotations/annotationProvider';
import type { Keys, PromoKeys } from './constants';
import type { CustomEditorTypes, WebviewTypes, WebviewViewTypes } from './constants.views';
Expand Down Expand Up @@ -26,10 +27,10 @@ export type ContextKeys = {
'gitlens:repos:withHostingIntegrations': string[];
'gitlens:repos:withHostingIntegrationsConnected': string[];
'gitlens:schemes:trackable': string[];
'gitlens:tabs:annotated': string[];
'gitlens:tabs:annotated:computing': string[];
'gitlens:tabs:blameable': string[];
'gitlens:tabs:tracked': string[];
'gitlens:tabs:annotated': Uri[];
'gitlens:tabs:annotated:computing': Uri[];
'gitlens:tabs:blameable': Uri[];
'gitlens:tabs:tracked': Uri[];
'gitlens:untrusted': boolean;
'gitlens:views:canCompare': boolean;
'gitlens:views:canCompare:file': boolean;
Expand Down
136 changes: 136 additions & 0 deletions src/system/vscode/uriMap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import type { Uri } from 'vscode';

type UriMapEntry<T> = {
readonly uri: Uri;
readonly value: T;
};

export class UriMap<T> implements Map<Uri, T> {
private static readonly defaultToKey = (resource: Uri) => resource.toString();

readonly [Symbol.toStringTag] = 'UriMap';
private readonly _map: Map<string, UriMapEntry<T>>;

constructor(entries?: readonly (readonly [Uri, T])[]) {
this._map = new Map();
if (entries?.length) {
for (const [uri, value] of entries) {
this.set(uri, value);
}
}
}

set(uri: Uri, value: T): this {
this._map.set(UriMap.defaultToKey(uri), { uri: uri, value: value });
return this;
}

get(uri: Uri): T | undefined {
return this._map.get(UriMap.defaultToKey(uri))?.value;
}

has(uri: Uri): boolean {
return this._map.has(UriMap.defaultToKey(uri));
}

get size(): number {
return this._map.size;
}

clear(): void {
this._map.clear();
}

delete(uri: Uri): boolean {
return this._map.delete(UriMap.defaultToKey(uri));
}

forEach(callbackfn: (value: T, key: Uri, map: Map<Uri, T>) => void, thisArg?: any): void {
if (typeof thisArg !== 'undefined') {
callbackfn = callbackfn.bind(thisArg);
}
for (const [_, entry] of this._map) {
callbackfn(entry.value, entry.uri, this);
}
}

*values(): MapIterator<T> {
for (const entry of this._map.values()) {
yield entry.value;
}
}

*keys(): MapIterator<Uri> {
for (const entry of this._map.values()) {
yield entry.uri;
}
}

*entries(): MapIterator<[Uri, T]> {
for (const entry of this._map.values()) {
yield [entry.uri, entry.value];
}
}

*[Symbol.iterator](): MapIterator<[Uri, T]> {
for (const [, entry] of this._map) {
yield [entry.uri, entry.value];
}
}
}

export class UriSet implements Set<Uri> {
readonly [Symbol.toStringTag]: string = 'UriSet';

private readonly _map: UriMap<Uri>;

constructor(entries?: readonly Uri[]) {
this._map = new UriMap();
if (entries?.length) {
for (const uri of entries) {
this.add(uri);
}
}
}

get size(): number {
return this._map.size;
}

add(uri: Uri): this {
this._map.set(uri, uri);
return this;
}

clear(): void {
this._map.clear();
}

delete(uri: Uri): boolean {
return this._map.delete(uri);
}

forEach(callbackfn: (value: Uri, value2: Uri, set: Set<Uri>) => void, thisArg?: any): void {
this._map.forEach((_value, key) => callbackfn.call(thisArg, key, key, this));
}

has(uri: Uri): boolean {
return this._map.has(uri);
}

entries(): SetIterator<[Uri, Uri]> {
return this._map.entries();
}

keys(): SetIterator<Uri> {
return this._map.keys();
}

values(): SetIterator<Uri> {
return this._map.keys();
}

[Symbol.iterator](): SetIterator<Uri> {
return this.keys();
}
}
23 changes: 0 additions & 23 deletions src/system/vscode/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,26 +352,3 @@ export function tabContainsUri(tab: Tab | undefined, uri: Uri | undefined): bool

return false;
}

const resourceContextKeyValueCache = new Map<string, Thenable<string>>();

export function getResourceContextKeyValue(uri: Uri) {
// If we are on a remote connection, VS Code's `TextDocument.uri` uses a `file://` scheme,
// but VS Code sets the `resource` context key to a "remote" url in the form of `vscode-remote://<remote-type>+<remote-host?>/<path>`
// So we need to try to generate that `vscode-remote://` version, which seems to work by getting the querystring from `env.asExternalUri`
if (uri.scheme === 'file' && env.remoteName) {
const uriKey = uri.toString();
let promise = resourceContextKeyValueCache.get(uriKey);
if (promise == null) {
promise = env.asExternalUri(uri).then(u => u.query);
resourceContextKeyValueCache.set(uriKey, promise);
promise.then(
() => resourceContextKeyValueCache.delete(uriKey),
() => resourceContextKeyValueCache.delete(uriKey),
);
}
return promise;
}

return uri.toString();
}
33 changes: 14 additions & 19 deletions src/trackers/documentTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import type { Deferrable } from '../system/function';
import { debounce } from '../system/function';
import { configuration } from '../system/vscode/configuration';
import { setContext } from '../system/vscode/context';
import { findTextDocument, getResourceContextKeyValue, isVisibleDocument } from '../system/vscode/utils';
import { UriSet } from '../system/vscode/uriMap';
import { findTextDocument, isVisibleDocument } from '../system/vscode/utils';
import type { TrackedGitDocument } from './trackedDocument';
import { createTrackedGitDocument } from './trackedDocument';

Expand Down Expand Up @@ -414,33 +415,33 @@ export class GitDocumentTracker implements Disposable {
(tracked ?? (await docPromise))?.dispose();
}

private readonly _openUrisBlameable = new Set<string>();
private readonly _openUrisTracked = new Set<string>();
private readonly _openUrisBlameable = new UriSet();
private readonly _openUrisTracked = new UriSet();
private _updateContextDebounced: Deferrable<() => void> | undefined;

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

function updateContextCore(this: GitDocumentTracker, key: string, blameable: boolean, tracked: boolean) {
function updateContextCore(this: GitDocumentTracker, uri: Uri, blameable: boolean, tracked: boolean) {
if (tracked) {
if (!this._openUrisTracked.has(key)) {
if (!this._openUrisTracked.has(uri)) {
changed = true;
this._openUrisTracked.add(key);
this._openUrisTracked.add(uri);
}
} else if (this._openUrisTracked.has(key)) {
} else if (this._openUrisTracked.has(uri)) {
changed = true;
this._openUrisTracked.delete(key);
this._openUrisTracked.delete(uri);
}

if (blameable) {
if (!this._openUrisBlameable.has(key)) {
if (!this._openUrisBlameable.has(uri)) {
changed = true;

this._openUrisBlameable.add(key);
this._openUrisBlameable.add(uri);
}
} else if (this._openUrisBlameable.has(key)) {
} else if (this._openUrisBlameable.has(uri)) {
changed = true;
this._openUrisBlameable.delete(key);
this._openUrisBlameable.delete(uri);
}

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

const key = getResourceContextKeyValue(uri);
if (typeof key !== 'string') {
void key.then(u => updateContextCore.call(this, u, blameable, tracked));
return;
}

updateContextCore.call(this, key, blameable, tracked);
updateContextCore.call(this, uri, blameable, tracked);
}

private fireDocumentDirtyStateChanged(e: DocumentDirtyStateChangeEvent) {
Expand Down
Loading