Skip to content

Commit 8d51d4a

Browse files
author
Eric Amodio
committed
Hides timeline view if no providers registered
Helps first-run experience, see here: #98614 (comment)
1 parent 264c39d commit 8d51d4a

File tree

3 files changed

+58
-7
lines changed

3 files changed

+58
-7
lines changed

extensions/git/src/timelineProvider.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,27 +65,32 @@ export class GitTimelineProvider implements TimelineProvider {
6565
readonly id = 'git-history';
6666
readonly label = localize('git.timeline.source', 'Git History');
6767

68-
private disposable: Disposable;
68+
private readonly disposable: Disposable;
69+
private providerDisposable: Disposable | undefined;
6970

7071
private repo: Repository | undefined;
7172
private repoDisposable: Disposable | undefined;
7273
private repoStatusDate: Date | undefined;
7374

74-
constructor(private readonly _model: Model) {
75+
constructor(private readonly model: Model) {
7576
this.disposable = Disposable.from(
76-
_model.onDidOpenRepository(this.onRepositoriesChanged, this),
77-
workspace.registerTimelineProvider(['file', 'git', 'vscode-remote', 'gitlens-git'], this),
77+
model.onDidOpenRepository(this.onRepositoriesChanged, this),
7878
);
79+
80+
if (model.repositories.length) {
81+
this.ensureProviderRegistration();
82+
}
7983
}
8084

8185
dispose() {
86+
this.providerDisposable?.dispose();
8287
this.disposable.dispose();
8388
}
8489

8590
async provideTimeline(uri: Uri, options: TimelineOptions, _token: CancellationToken): Promise<Timeline> {
8691
// console.log(`GitTimelineProvider.provideTimeline: uri=${uri} state=${this._model.state}`);
8792

88-
const repo = this._model.getRepository(uri);
93+
const repo = this.model.getRepository(uri);
8994
if (!repo) {
9095
this.repoDisposable?.dispose();
9196
this.repoStatusDate = undefined;
@@ -110,7 +115,7 @@ export class GitTimelineProvider implements TimelineProvider {
110115
let limit: number | undefined;
111116
if (options.limit !== undefined && typeof options.limit !== 'number') {
112117
try {
113-
const result = await this._model.git.exec(repo.root, ['rev-list', '--count', `${options.limit.id}..`, '--', uri.fsPath]);
118+
const result = await this.model.git.exec(repo.root, ['rev-list', '--count', `${options.limit.id}..`, '--', uri.fsPath]);
114119
if (!result.exitCode) {
115120
// Ask for 2 more (1 for the limit commit and 1 for the next commit) than so we can determine if there are more commits
116121
limit = Number(result.stdout) + 2;
@@ -203,9 +208,17 @@ export class GitTimelineProvider implements TimelineProvider {
203208
};
204209
}
205210

211+
private ensureProviderRegistration() {
212+
if (this.providerDisposable === undefined) {
213+
this.providerDisposable = workspace.registerTimelineProvider(['file', 'git', 'vscode-remote', 'gitlens-git'], this);
214+
}
215+
}
216+
206217
private onRepositoriesChanged(_repo: Repository) {
207218
// console.log(`GitTimelineProvider.onRepositoriesChanged`);
208219

220+
this.ensureProviderRegistration();
221+
209222
// TODO@eamodio: Being naive for now and just always refreshing each time there is a new repository
210223
this.fireChanged();
211224
}

src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
1010
import { IViewsRegistry, IViewDescriptor, Extensions as ViewExtensions } from 'vs/workbench/common/views';
1111
import { VIEW_CONTAINER } from 'vs/workbench/contrib/files/browser/explorerViewlet';
1212
import { ITimelineService, TimelinePaneId } from 'vs/workbench/contrib/timeline/common/timeline';
13-
import { TimelineService } from 'vs/workbench/contrib/timeline/common/timelineService';
13+
import { TimelineHasProviderContext, TimelineService } from 'vs/workbench/contrib/timeline/common/timelineService';
1414
import { TimelinePane } from './timelinePane';
1515
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
1616
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
@@ -30,6 +30,7 @@ export class TimelinePaneDescriptor implements IViewDescriptor {
3030
readonly canToggleVisibility = true;
3131
readonly hideByDefault = false;
3232
readonly canMoveView = true;
33+
readonly when = TimelineHasProviderContext;
3334

3435
focusCommand = { id: 'timeline.focus' };
3536
}

src/vs/workbench/contrib/timeline/common/timelineService.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import { URI } from 'vs/base/common/uri';
1111
import { ILogService } from 'vs/platform/log/common/log';
1212
import { ITimelineService, TimelineChangeEvent, TimelineOptions, TimelineProvidersChangeEvent, TimelineProvider, InternalTimelineOptions, TimelinePaneId } from './timeline';
1313
import { IViewsService } from 'vs/workbench/common/views';
14+
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
15+
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
16+
17+
export const TimelineHasProviderContext = new RawContextKey<boolean>('timelineHasProvider', false);
1418

1519
export class TimelineService implements ITimelineService {
1620
declare readonly _serviceBrand: undefined;
@@ -23,13 +27,30 @@ export class TimelineService implements ITimelineService {
2327
private readonly _onDidChangeUri = new Emitter<URI>();
2428
readonly onDidChangeUri: Event<URI> = this._onDidChangeUri.event;
2529

30+
private excludedSources: Set<string>;
31+
private readonly hasProviderContext: IContextKey<boolean>;
2632
private readonly providers = new Map<string, TimelineProvider>();
2733
private readonly providerSubscriptions = new Map<string, IDisposable>();
2834

2935
constructor(
3036
@ILogService private readonly logService: ILogService,
3137
@IViewsService protected viewsService: IViewsService,
38+
@IConfigurationService protected configurationService: IConfigurationService,
39+
@IContextKeyService protected contextKeyService: IContextKeyService,
3240
) {
41+
this.hasProviderContext = TimelineHasProviderContext.bindTo(this.contextKeyService);
42+
43+
this.excludedSources = new Set(configurationService.getValue('timeline.excludeSources'));
44+
configurationService.onDidChangeConfiguration(e => {
45+
if (e.affectsConfiguration('timeline.excludeSources')) {
46+
this.excludedSources = new Set(this.configurationService.getValue('timeline.excludeSources'));
47+
48+
this.updateHasProviderContext();
49+
}
50+
}, this);
51+
52+
this.updateHasProviderContext();
53+
3354
// let source = 'fast-source';
3455
// this.registerTimelineProvider({
3556
// scheme: '*',
@@ -213,6 +234,9 @@ export class TimelineService implements ITimelineService {
213234
}
214235

215236
this.providers.set(id, provider);
237+
238+
this.updateHasProviderContext();
239+
216240
if (provider.onDidChange) {
217241
this.providerSubscriptions.set(id, provider.onDidChange(e => this._onDidChangeTimeline.fire(e)));
218242
}
@@ -235,11 +259,24 @@ export class TimelineService implements ITimelineService {
235259

236260
this.providers.delete(id);
237261
this.providerSubscriptions.delete(id);
262+
263+
this.updateHasProviderContext();
264+
238265
this._onDidChangeProviders.fire({ removed: [id] });
239266
}
240267

241268
setUri(uri: URI) {
242269
this.viewsService.openView(TimelinePaneId, true);
243270
this._onDidChangeUri.fire(uri);
244271
}
272+
273+
private updateHasProviderContext() {
274+
if (this.providers.size === 0) {
275+
this.hasProviderContext.set(false);
276+
return;
277+
}
278+
279+
const hasProviders = [...this.providers.keys()].some(id => !this.excludedSources.has(id));
280+
this.hasProviderContext.set(hasProviders);
281+
}
245282
}

0 commit comments

Comments
 (0)