diff --git a/extensions/git/package.json b/extensions/git/package.json index 6f417a43e75..ad8def877ce 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -496,7 +496,6 @@ { "command": "git.branch", "title": "%command.branch%", - "icon": "$(plus)", "category": "Git", "enablement": "!operationInProgress" }, @@ -1046,6 +1045,20 @@ "title": "%command.graphCompareRef%", "category": "Git", "enablement": "!operationInProgress" + }, + { + "command": "git.repositories.createBranch", + "title": "%command.branch%", + "icon": "$(plus)", + "category": "Git", + "enablement": "!operationInProgress" + }, + { + "command": "git.repositories.createTag", + "title": "%command.createTag%", + "icon": "$(plus)", + "category": "Git", + "enablement": "!operationInProgress" } ], "continueEditSession": [ @@ -1681,6 +1694,14 @@ { "command": "git.repositories.compareRef", "when": "false" + }, + { + "command": "git.repositories.createBranch", + "when": "false" + }, + { + "command": "git.repositories.createTag", + "when": "false" } ], "scm/title": [ @@ -1873,12 +1894,12 @@ ], "scm/artifactGroup/context": [ { - "command": "git.branch", + "command": "git.repositories.createBranch", "group": "inline@1", "when": "scmProvider == git && scmArtifactGroup == branches" }, { - "command": "git.createTag", + "command": "git.repositories.createTag", "group": "inline@1", "when": "scmProvider == git && scmArtifactGroup == tags" } diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index d620679eeb6..621d1439ec8 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -3363,24 +3363,7 @@ export class CommandCenter { @command('git.createTag', { repository: true }) async createTag(repository: Repository, historyItem?: SourceControlHistoryItem): Promise { - const inputTagName = await window.showInputBox({ - placeHolder: l10n.t('Tag name'), - prompt: l10n.t('Please provide a tag name'), - ignoreFocusOut: true - }); - - if (!inputTagName) { - return; - } - - const inputMessage = await window.showInputBox({ - placeHolder: l10n.t('Message'), - prompt: l10n.t('Please provide a message to annotate the tag'), - ignoreFocusOut: true - }); - - const name = inputTagName.replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$/g, '-'); - await repository.tag({ name, message: inputMessage, ref: historyItem?.id }); + await this._createTag(repository, historyItem?.id); } @command('git.deleteTag', { repository: true }) @@ -5259,6 +5242,24 @@ export class CommandCenter { config.update(setting, !enabled, true); } + @command('git.repositories.createBranch', { repository: true }) + async artifactGroupCreateBranch(repository: Repository): Promise { + if (!repository) { + return; + } + + await this._branch(repository, undefined, false); + } + + @command('git.repositories.createTag', { repository: true }) + async artifactGroupCreateTag(repository: Repository): Promise { + if (!repository) { + return; + } + + await this._createTag(repository); + } + @command('git.repositories.checkout', { repository: true }) async artifactCheckout(repository: Repository, artifact: SourceControlArtifact): Promise { if (!repository || !artifact) { @@ -5312,6 +5313,27 @@ export class CommandCenter { `${sourceRef.ref.name} ↔ ${artifact.name}`); } + private async _createTag(repository: Repository, ref?: string): Promise { + const inputTagName = await window.showInputBox({ + placeHolder: l10n.t('Tag name'), + prompt: l10n.t('Please provide a tag name'), + ignoreFocusOut: true + }); + + if (!inputTagName) { + return; + } + + const inputMessage = await window.showInputBox({ + placeHolder: l10n.t('Message'), + prompt: l10n.t('Please provide a message to annotate the tag'), + ignoreFocusOut: true + }); + + const name = inputTagName.replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$/g, '-'); + await repository.tag({ name, message: inputMessage, ref }); + } + private createCommand(id: string, key: string, method: Function, options: ScmCommandOptions): (...args: any[]) => any { const result = (...args: any[]) => { let result: Promise; diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css index 3e1055b474c..eba1c7aab06 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scm.css +++ b/src/vs/workbench/contrib/scm/browser/media/scm.css @@ -538,6 +538,11 @@ .scm-repositories-view .scm-artifact-group, .scm-repositories-view .scm-artifact { display: flex; + align-items: center; + + .icon { + margin-right: 2px; + } } .scm-repositories-view .scm-artifact-group .monaco-icon-label, @@ -545,10 +550,6 @@ flex-grow: 1; } -.scm-repositories-view .scm-artifact .monaco-icon-label-container { - display: flex; -} - .scm-repositories-view .scm-artifact-group .monaco-highlighted-label, .scm-repositories-view .scm-artifact .monaco-highlighted-label { display: flex; diff --git a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts index 55a39c09022..02f10de23a1 100644 --- a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts @@ -43,6 +43,7 @@ import { basename } from '../../../../base/common/resources.js'; import { ICompressibleTreeRenderer } from '../../../../base/browser/ui/tree/objectTree.js'; import { ICompressedTreeNode } from '../../../../base/browser/ui/tree/compressedObjectTreeModel.js'; import { ITreeCompressionDelegate } from '../../../../base/browser/ui/tree/asyncDataTree.js'; +import { Codicon } from '../../../../base/common/codicons.js'; type TreeElement = ISCMRepository | SCMArtifactGroupTreeElement | SCMArtifactTreeElement | IResourceNode; @@ -66,6 +67,7 @@ class ListDelegate implements IListVirtualDelegate { } interface ArtifactGroupTemplate { + readonly icon: HTMLElement; readonly label: IconLabel; readonly actionBar: WorkbenchToolBar; readonly elementDisposables: DisposableStore; @@ -89,21 +91,23 @@ class ArtifactGroupRenderer implements ICompressibleTreeRenderer, index: number, templateData: ArtifactGroupTemplate): void { const provider = node.element.repository.provider; const artifactGroup = node.element.artifactGroup; - const artifactGroupIcon = ThemeIcon.isThemeIcon(artifactGroup.icon) - ? `$(${artifactGroup.icon.id}) ` : ''; - templateData.label.setLabel(`${artifactGroupIcon}${artifactGroup.name}`); + templateData.icon.className = ThemeIcon.isThemeIcon(artifactGroup.icon) + ? `icon ${ThemeIcon.asClassName(artifactGroup.icon)}` + : ''; + templateData.label.setLabel(artifactGroup.name); const repositoryMenus = this._scmViewService.menus.getRepositoryMenus(provider); templateData.elementDisposables.add(connectPrimaryMenu(repositoryMenus.getArtifactGroupMenu(artifactGroup), primary => { @@ -127,6 +131,7 @@ class ArtifactGroupRenderer implements ICompressibleTreeRenderer, FuzzyScore>, index: number, templateData: ArtifactTemplate): void { @@ -163,19 +169,21 @@ class ArtifactRenderer implements ICompressibleTreeRenderer>, FuzzyScore>, index: number, templateData: ArtifactTemplate, details?: ITreeElementRenderDetails): void { - const compressed = node.element as ICompressedTreeNode>; - const folder = compressed.elements[compressed.elements.length - 1]; - templateData.label.setLabel(`$(folder) ${folder.uri.fsPath.substring(1)}`); + const compressed = node.element; + const artifactOrFolder = compressed.elements[compressed.elements.length - 1]; - templateData.actionBar.setActions([]); - templateData.actionBar.context = undefined; + if (isSCMArtifactTreeElement(artifactOrFolder)) { + const artifact = artifactOrFolder.artifact; + + templateData.icon.className = ThemeIcon.isThemeIcon(artifactOrFolder.group.icon) + ? `icon ${ThemeIcon.asClassName(artifactOrFolder.group.icon)}` + : ''; + templateData.label.setLabel(artifact.name, artifact.description); + + const provider = artifactOrFolder.repository.provider; + const repositoryMenus = this._scmViewService.menus.getRepositoryMenus(provider); + templateData.elementDisposables.add(connectPrimaryMenu(repositoryMenus.getArtifactMenu(artifactOrFolder.group), primary => { + templateData.actionBar.setActions(primary); + }, 'inline', provider)); + templateData.actionBar.context = artifact; + } else if (ResourceTree.isResourceNode(artifactOrFolder)) { + templateData.icon.className = `icon ${ThemeIcon.asClassName(Codicon.folder)}`; + templateData.label.setLabel(artifactOrFolder.uri.fsPath.substring(1)); + + templateData.actionBar.setActions([]); + templateData.actionBar.context = undefined; + } } disposeElement(element: ITreeNode, FuzzyScore>, index: number, templateData: ArtifactTemplate, details?: ITreeElementRenderDetails): void { @@ -306,10 +332,10 @@ class RepositoryTreeIdentityProvider implements IIdentityProvider { class RepositoriesTreeCompressionDelegate implements ITreeCompressionDelegate { isIncompressible(element: TreeElement): boolean { if (ResourceTree.isResourceNode(element)) { - return element.childrenCount === 0 || !element.parent || !element.parent.parent; + return element.childrenCount > 1; + } else { + return true; } - - return true; } } @@ -440,18 +466,12 @@ export class SCMRepositoriesViewPane extends ViewPane { } // Explorer mode - // Expand artifact folders with one child only if (isSCMArtifactNode(e)) { - if (e.childrenCount !== 1) { - return true; - } - - // Check if the only child is a leaf node - const firstChild = Iterable.first(e.children); - return firstChild?.element !== undefined; + // Only expand artifact folders as they are compressed by default + return !(e.childrenCount === 1 && Iterable.first(e.children)?.element === undefined); + } else { + return true; } - - return true; }, compressionEnabled: true, overrideStyles: this.getLocationBasedColors().listOverrideStyles,