diff --git a/extensions/git/package.json b/extensions/git/package.json index 202d73c4c00..848ab1d46c9 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -396,11 +396,11 @@ }, { "command": "git.clean", - "when": "config.git.enabled && gitOpenRepositoryCount != 0" + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && !gitFreshRepository" }, { "command": "git.cleanAll", - "when": "config.git.enabled && gitOpenRepositoryCount != 0" + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && !gitFreshRepository" }, { "command": "git.commit", @@ -631,7 +631,7 @@ { "command": "git.cleanAll", "group": "4_stage", - "when": "scmProvider == git" + "when": "scmProvider == git && !gitFreshRepository" }, { "command": "git.stashIncludeUntracked", @@ -689,7 +689,7 @@ }, { "command": "git.cleanAll", - "when": "scmProvider == git && scmResourceGroup == workingTree", + "when": "scmProvider == git && scmResourceGroup == workingTree && !gitFreshRepository", "group": "1_modification" }, { @@ -699,7 +699,7 @@ }, { "command": "git.cleanAll", - "when": "scmProvider == git && scmResourceGroup == workingTree", + "when": "scmProvider == git && scmResourceGroup == workingTree && !gitFreshRepository", "group": "inline" }, { @@ -781,12 +781,12 @@ }, { "command": "git.clean", - "when": "scmProvider == git && scmResourceGroup == workingTree", + "when": "scmProvider == git && scmResourceGroup == workingTree && !gitFreshRepository", "group": "1_modification" }, { "command": "git.clean", - "when": "scmProvider == git && scmResourceGroup == workingTree", + "when": "scmProvider == git && scmResourceGroup == workingTree && !gitFreshRepository", "group": "inline" }, { diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 893b2ce2939..34834269b92 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -5,7 +5,7 @@ 'use strict'; -import { Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData, Memento, SourceControlInputBoxValidationType } from 'vscode'; +import { commands, Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData, Memento, SourceControlInputBoxValidationType } from 'vscode'; import { Repository as BaseRepository, Ref, Branch, Remote, Commit, GitErrorCodes, Stash, RefType, GitError, Submodule, DiffOptions } from './git'; import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent } from './util'; import { memoize, throttle, debounce } from './decorators'; @@ -1041,13 +1041,9 @@ export class Repository implements Disposable { this._sourceControl.count = count; - // set context key - let stateContextKey = ''; - - switch (this.state) { - case RepositoryState.Idle: stateContextKey = 'idle'; break; - case RepositoryState.Disposed: stateContextKey = 'norepo'; break; - } + // Disable `Discard All Changes` for "fresh" repositories + // https://github.com/Microsoft/vscode/issues/43066 + commands.executeCommand('setContext', 'gitFreshRepository', !this._HEAD || !this._HEAD.commit); this._onDidChangeStatus.fire(); } diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index d9595cfe31c..cbdb78d5d37 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -37,7 +37,7 @@ import { fillInActions, ContextAwareMenuItemActionItem } from 'vs/platform/actio import { SCMMenus } from './scmMenus'; import { ActionBar, IActionItemProvider, Separator, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IThemeService, LIGHT } from 'vs/platform/theme/common/themeService'; -import { isSCMResource } from './scmUtil'; +import { isSCMResource, getSCMResourceContextKey } from './scmUtil'; import { attachBadgeStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -367,9 +367,11 @@ class ResourceGroupRenderer implements IRenderer { + const primary: IAction[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; + fillInActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => /^inline/.test(g)); + + template.actionBar.clear(); + template.actionBar.push(primary, { icon: true, label: false }); + }; + + menu.onDidChange(updateActions, null, disposables); + updateActions(); const updateCount = () => template.count.setCount(group.elements.length); - template.elementDisposable = group.onDidSplice(updateCount); + group.onDidSplice(updateCount, null, disposables); updateCount(); + + template.elementDisposable = combinedDisposable(disposables); } disposeTemplate(template: ResourceGroupTemplate): void { @@ -446,12 +473,13 @@ class ResourceRenderer implements IRenderer { get templateId(): string { return ResourceRenderer.TEMPLATE_ID; } constructor( - private scmMenus: SCMMenus, private actionItemProvider: IActionItemProvider, private getSelectedResources: () => ISCMResource[], - @IThemeService private themeService: IThemeService, - @IInstantiationService private instantiationService: IInstantiationService, - @IConfigurationService private configurationService: IConfigurationService + private themeService: IThemeService, + private instantiationService: IInstantiationService, + private contextKeyService: IContextKeyService, + private contextMenuService: IContextMenuService, + private menuService: IMenuService ) { } renderTemplate(container: HTMLElement): ResourceTemplate { @@ -483,12 +511,28 @@ class ResourceRenderer implements IRenderer { template.fileLabel.setFile(resource.sourceUri, { fileDecorations: { colors: false, badges: !icon, data: resource.decorations } }); template.actionBar.context = resource; + const disposables: IDisposable[] = []; + + const contextKeyService = this.contextKeyService.createScoped(); + disposables.push(contextKeyService); + + contextKeyService.createKey('scmProvider', resource.resourceGroup.provider.contextValue); + contextKeyService.createKey('scmResourceGroup', getSCMResourceContextKey(resource.resourceGroup)); + + const menu = this.menuService.createMenu(MenuId.SCMResourceGroupContext, contextKeyService); + disposables.push(menu); + const updateActions = () => { + const primary: IAction[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; + fillInActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => /^inline/.test(g)); + template.actionBar.clear(); - template.actionBar.push(this.scmMenus.getResourceActions(resource), { icon: true, label: false }); + template.actionBar.push(primary, { icon: true, label: false }); }; - template.elementDisposable = this.configurationService.onDidChangeConfiguration(updateActions); + menu.onDidChange(updateActions, null, disposables); updateActions(); toggleClass(template.name, 'strike-through', resource.decorations.strikeThrough); @@ -504,6 +548,7 @@ class ResourceRenderer implements IRenderer { } template.element.setAttribute('data-tooltip', resource.decorations.tooltip); + template.elementDisposable = combinedDisposable(disposables); } disposeTemplate(template: ResourceTemplate): void { @@ -687,6 +732,10 @@ export class RepositoryPanel extends ViewletPanel { private menus: SCMMenus; private visibilityDisposables: IDisposable[] = []; + get onDidChangeTitle(): Event { + return this.menus.onDidChangeTitle; + } + constructor( readonly repository: ISCMRepository, private viewModel: IViewModel, @@ -699,7 +748,9 @@ export class RepositoryPanel extends ViewletPanel { @IWorkbenchEditorService protected editorService: IWorkbenchEditorService, @IEditorGroupService protected editorGroupService: IEditorGroupService, @IInstantiationService protected instantiationService: IInstantiationService, - @IConfigurationService protected configurationService: IConfigurationService + @IConfigurationService protected configurationService: IConfigurationService, + @IContextKeyService protected contextKeyService: IContextKeyService, + @IMenuService protected menuService: IMenuService ) { super(repository.provider.label, {}, keybindingService, contextMenuService); this.menus = instantiationService.createInstance(SCMMenus, repository.provider); @@ -811,8 +862,8 @@ export class RepositoryPanel extends ViewletPanel { const actionItemProvider = (action: IAction) => this.getActionItem(action); const renderers = [ - new ResourceGroupRenderer(this.menus, actionItemProvider, this.themeService), - this.instantiationService.createInstance(ResourceRenderer, this.menus, actionItemProvider, () => this.getSelectedResources()), + new ResourceGroupRenderer(actionItemProvider, this.themeService, this.contextKeyService, this.contextMenuService, this.menuService), + new ResourceRenderer(actionItemProvider, () => this.getSelectedResources(), this.themeService, this.instantiationService, this.contextKeyService, this.contextMenuService, this.menuService) ]; this.list = this.instantiationService.createInstance(WorkbenchList, this.listContainer, delegate, renderers, { @@ -978,6 +1029,7 @@ export class SCMViewlet extends PanelViewlet implements IViewModel { private mainPanelDisposable: IDisposable = EmptyDisposable; private _repositories: ISCMRepository[] = []; private repositoryPanels: RepositoryPanel[] = []; + private singleRepositoryPanelTitleActionsDisposable: IDisposable = EmptyDisposable; private disposables: IDisposable[] = []; private _onDidSplice = new Emitter>(); @@ -1162,6 +1214,8 @@ export class SCMViewlet extends PanelViewlet implements IViewModel { } private onSelectionChange(repositories: ISCMRepository[]): void { + const wasSingleView = this.isSingleView(); + // Collect unselected panels const panelsToRemove = this.repositoryPanels .filter(p => repositories.every(r => p.repository !== r)); @@ -1195,10 +1249,20 @@ export class SCMViewlet extends PanelViewlet implements IViewModel { const height = typeof this.height === 'number' ? this.height : 1000; const mainPanelHeight = this.getPanelSize(this.mainPanel); const size = (height - mainPanelHeight) / repositories.length; - for (const panel of this.repositoryPanels) { this.resizePanel(panel, size); } + + // React to menu changes for single view mode + if (wasSingleView !== this.isSingleView()) { + this.singleRepositoryPanelTitleActionsDisposable.dispose(); + + if (this.isSingleView()) { + this.singleRepositoryPanelTitleActionsDisposable = this.repositoryPanels[0].onDidChangeTitle(this.updateTitleArea, this); + } + + this.updateTitleArea(); + } } protected isSingleView(): boolean {