mirror of
https://github.com/Microsoft/vscode
synced 2024-10-05 19:02:54 +00:00
SCM - extract activity count badge into its own workbench contribution (#214610)
This commit is contained in:
parent
d08886ba68
commit
88f50e0809
|
@ -230,10 +230,9 @@ class MainThreadSCMProvider implements ISCMProvider, QuickDiffProvider {
|
|||
get acceptInputCommand(): Command | undefined { return this.features.acceptInputCommand; }
|
||||
get actionButton(): ISCMActionButtonDescriptor | undefined { return this.features.actionButton ?? undefined; }
|
||||
get statusBarCommands(): Command[] | undefined { return this.features.statusBarCommands; }
|
||||
get count(): number | undefined { return this.features.count; }
|
||||
|
||||
private readonly _countObs = observableValue<number | undefined>(this, undefined);
|
||||
get countObs() { return this._countObs; }
|
||||
private readonly _count = observableValue<number | undefined>(this, undefined);
|
||||
get count() { return this._count; }
|
||||
|
||||
private readonly _statusBarCommandsObs = observableValue<readonly Command[] | undefined>(this, undefined);
|
||||
get statusBarCommandsObs() { return this._statusBarCommandsObs; }
|
||||
|
@ -288,7 +287,7 @@ class MainThreadSCMProvider implements ISCMProvider, QuickDiffProvider {
|
|||
}
|
||||
|
||||
if (typeof features.count !== 'undefined') {
|
||||
this._countObs.set(features.count, undefined);
|
||||
this._count.set(features.count, undefined);
|
||||
}
|
||||
|
||||
if (typeof features.statusBarCommands !== 'undefined') {
|
||||
|
|
|
@ -22,9 +22,103 @@ import { ITitleService } from 'vs/workbench/services/title/browser/titleService'
|
|||
import { IEditorGroupContextKeyProvider, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
import { getRepositoryResourceCount } from 'vs/workbench/contrib/scm/browser/util';
|
||||
import { autorunWithStore, derived, derivedObservableWithCache, IObservable, observableFromEvent } from 'vs/base/common/observable';
|
||||
import { observableConfigValue } from 'vs/platform/observable/common/platformObservableUtils';
|
||||
|
||||
function getCount(repository: ISCMRepository): number {
|
||||
return repository.provider.count ?? getRepositoryResourceCount(repository.provider);
|
||||
export class SCMActivityCountBadgeController extends Disposable implements IWorkbenchContribution {
|
||||
private readonly _countBadgeConfig = observableConfigValue<'all' | 'focused' | 'off'>('scm.countBadge', 'all', this.configurationService);
|
||||
|
||||
private readonly _repositories = observableFromEvent(
|
||||
Event.any(this.scmService.onDidAddRepository, this.scmService.onDidRemoveRepository),
|
||||
() => this.scmService.repositories);
|
||||
|
||||
private readonly _focusedRepository = observableFromEvent(
|
||||
this.scmViewService.onDidFocusRepository,
|
||||
() => this.scmViewService.focusedRepository ? Object.create(this.scmViewService.focusedRepository) : undefined);
|
||||
|
||||
private readonly _activeEditor = observableFromEvent(
|
||||
this.editorService.onDidActiveEditorChange,
|
||||
() => this.editorService.activeEditor);
|
||||
|
||||
private readonly _activeEditorRepository = derived(reader => {
|
||||
const activeResource = EditorResourceAccessor.getOriginalUri(this._activeEditor.read(reader));
|
||||
if (!activeResource) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.scmService.getRepository(activeResource);
|
||||
});
|
||||
|
||||
private readonly _activeRepository = derivedObservableWithCache<ISCMRepository | undefined>(this, (reader, lastValue) => {
|
||||
const focusedRepository = this._focusedRepository.read(reader);
|
||||
if (focusedRepository && focusedRepository.id !== lastValue?.id) {
|
||||
return focusedRepository;
|
||||
}
|
||||
|
||||
const activeEditorRepository = this._activeEditorRepository.read(reader);
|
||||
if (activeEditorRepository && activeEditorRepository.id !== lastValue?.id) {
|
||||
return activeEditorRepository;
|
||||
}
|
||||
|
||||
return lastValue;
|
||||
});
|
||||
|
||||
private readonly _countBadgeRepositories = derived(reader => {
|
||||
switch (this._countBadgeConfig.read(reader)) {
|
||||
case 'all': {
|
||||
const repositories = this._repositories.read(reader);
|
||||
return [...Iterable.map(repositories, r => ({ ...r.provider, resourceCount: this._getRepositoryResourceCount(r) }))];
|
||||
}
|
||||
case 'focused': {
|
||||
const repository = this._activeRepository.read(reader);
|
||||
return repository ? [{ ...repository.provider, resourceCount: this._getRepositoryResourceCount(repository) }] : [];
|
||||
}
|
||||
case 'off':
|
||||
return [];
|
||||
default:
|
||||
throw new Error('Invalid countBadge setting');
|
||||
}
|
||||
});
|
||||
|
||||
private readonly _countBadge = derived(reader => {
|
||||
let total = 0;
|
||||
|
||||
for (const repository of this._countBadgeRepositories.read(reader)) {
|
||||
const count = repository.count?.read(reader);
|
||||
const resourceCount = repository.resourceCount.read(reader);
|
||||
|
||||
total = total + (count ?? resourceCount);
|
||||
}
|
||||
|
||||
return total;
|
||||
});
|
||||
|
||||
constructor(
|
||||
@IActivityService private readonly activityService: IActivityService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@ISCMService private readonly scmService: ISCMService,
|
||||
@ISCMViewService private readonly scmViewService: ISCMViewService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(autorunWithStore((reader, store) => {
|
||||
this._renderActivityCount(this._countBadge.read(reader), store);
|
||||
}));
|
||||
}
|
||||
|
||||
private _getRepositoryResourceCount(repository: ISCMRepository): IObservable<number> {
|
||||
return observableFromEvent(repository.provider.onDidChangeResources, () => getRepositoryResourceCount(repository.provider));
|
||||
}
|
||||
|
||||
private _renderActivityCount(count: number, store: DisposableStore): void {
|
||||
if (count === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const badge = new NumberBadge(count, num => localize('scmPendingChangesBadge', '{0} pending changes', num));
|
||||
store.add(this.activityService.showViewActivity(VIEW_PANE_ID, { badge }));
|
||||
}
|
||||
}
|
||||
|
||||
export class SCMStatusController implements IWorkbenchContribution {
|
||||
|
@ -40,16 +134,11 @@ export class SCMStatusController implements IWorkbenchContribution {
|
|||
@ISCMService private readonly scmService: ISCMService,
|
||||
@ISCMViewService private readonly scmViewService: ISCMViewService,
|
||||
@IStatusbarService private readonly statusbarService: IStatusbarService,
|
||||
@IActivityService private readonly activityService: IActivityService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
@IEditorService private readonly editorService: IEditorService
|
||||
) {
|
||||
this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables);
|
||||
this.scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables);
|
||||
|
||||
const onDidChangeSCMCountBadge = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.countBadge'));
|
||||
onDidChangeSCMCountBadge(this.renderActivityCount, this, this.disposables);
|
||||
|
||||
for (const repository of this.scmService.repositories) {
|
||||
this.onDidAddRepository(repository);
|
||||
}
|
||||
|
@ -58,7 +147,6 @@ export class SCMStatusController implements IWorkbenchContribution {
|
|||
this.focusRepository(this.scmViewService.focusedRepository);
|
||||
|
||||
editorService.onDidActiveEditorChange(() => this.tryFocusRepositoryBasedOnActiveEditor(), this, this.disposables);
|
||||
this.renderActivityCount();
|
||||
}
|
||||
|
||||
private tryFocusRepositoryBasedOnActiveEditor(repositories: Iterable<ISCMRepository> = this.scmService.repositories): boolean {
|
||||
|
@ -78,17 +166,13 @@ export class SCMStatusController implements IWorkbenchContribution {
|
|||
}
|
||||
|
||||
private onDidAddRepository(repository: ISCMRepository): void {
|
||||
const onDidChange = Event.any(repository.provider.onDidChange, repository.provider.onDidChangeResources);
|
||||
const changeDisposable = onDidChange(() => this.renderActivityCount());
|
||||
|
||||
const onDidRemove = Event.filter(this.scmService.onDidRemoveRepository, e => e === repository);
|
||||
const removeDisposable = onDidRemove(() => {
|
||||
disposable.dispose();
|
||||
this.repositoryDisposables.delete(disposable);
|
||||
this.renderActivityCount();
|
||||
});
|
||||
|
||||
const disposable = combinedDisposable(changeDisposable, removeDisposable);
|
||||
const disposable = combinedDisposable(removeDisposable);
|
||||
this.repositoryDisposables.add(disposable);
|
||||
|
||||
this.tryFocusRepositoryBasedOnActiveEditor(Iterable.single(repository));
|
||||
|
@ -115,7 +199,6 @@ export class SCMStatusController implements IWorkbenchContribution {
|
|||
}
|
||||
|
||||
this.renderStatusBar(repository);
|
||||
this.renderActivityCount();
|
||||
}
|
||||
|
||||
private renderStatusBar(repository: ISCMRepository | undefined): void {
|
||||
|
@ -166,25 +249,6 @@ export class SCMStatusController implements IWorkbenchContribution {
|
|||
this.statusBarDisposable = disposables;
|
||||
}
|
||||
|
||||
private renderActivityCount(): void {
|
||||
const countBadgeType = this.configurationService.getValue<'all' | 'focused' | 'off'>('scm.countBadge');
|
||||
|
||||
let count = 0;
|
||||
|
||||
if (countBadgeType === 'all') {
|
||||
count = Iterable.reduce(this.scmService.repositories, (r, repository) => r + getCount(repository), 0);
|
||||
} else if (countBadgeType === 'focused' && this.focusedRepository) {
|
||||
count = getCount(this.focusedRepository);
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
const badge = new NumberBadge(count, num => localize('scmPendingChangesBadge', '{0} pending changes', num));
|
||||
this.badgeDisposable.value = this.activityService.showViewActivity(VIEW_PANE_ID, { badge });
|
||||
} else {
|
||||
this.badgeDisposable.value = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.focusDisposable.dispose();
|
||||
this.statusBarDisposable.dispose();
|
||||
|
|
|
@ -10,7 +10,7 @@ import { DirtyDiffWorkbenchController } from './dirtydiffDecorator';
|
|||
import { VIEWLET_ID, ISCMService, VIEW_PANE_ID, ISCMProvider, ISCMViewService, REPOSITORIES_VIEW_PANE_ID } from 'vs/workbench/contrib/scm/common/scm';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { SCMActiveRepositoryContextKeyController, SCMActiveResourceContextKeyController, SCMStatusController } from './activity';
|
||||
import { SCMActiveRepositoryContextKeyController, SCMActiveResourceContextKeyController, SCMActivityCountBadgeController, SCMStatusController } from './activity';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
@ -112,6 +112,9 @@ viewsRegistry.registerViews([{
|
|||
containerIcon: sourceControlViewIcon
|
||||
}], viewContainer);
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
|
||||
.registerWorkbenchContribution(SCMActivityCountBadgeController, LifecyclePhase.Restored);
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
|
||||
.registerWorkbenchContribution(SCMActiveResourceContextKeyController, LifecyclePhase.Restored);
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ export class RepositoryRenderer implements ICompressibleTreeRenderer<ISCMReposit
|
|||
}));
|
||||
|
||||
templateData.elementDisposables.add(autorun(reader => {
|
||||
const count = repository.provider.countObs.read(reader) ?? getRepositoryResourceCount(repository.provider);
|
||||
const count = repository.provider.count.read(reader) ?? getRepositoryResourceCount(repository.provider);
|
||||
templateData.countContainer.setAttribute('data-count', String(count));
|
||||
templateData.count.setCount(count);
|
||||
}));
|
||||
|
|
|
@ -73,8 +73,7 @@ export interface ISCMProvider extends IDisposable {
|
|||
|
||||
readonly rootUri?: URI;
|
||||
readonly inputBoxTextModel: ITextModel;
|
||||
readonly count?: number;
|
||||
readonly countObs: IObservable<number | undefined>;
|
||||
readonly count: IObservable<number | undefined>;
|
||||
readonly commitTemplate: IObservable<string>;
|
||||
readonly historyProvider?: ISCMHistoryProvider;
|
||||
readonly onDidChangeHistoryProvider: Event<void>;
|
||||
|
|
Loading…
Reference in a new issue