diff --git a/package-lock.json b/package-lock.json index 80357c23f..ea1a41e6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "license": "ISC", "devDependencies": { "@cloudflare/workers-types": "^5.20260910.1", - "@github1s/vscode-web": "^0.30.2", + "@github1s/vscode-web": "^0.30.3", "@types/node": "^22.10.5", "chokidar": "^4.0.3", "clean-css": "^5.3.3", @@ -252,9 +252,9 @@ } }, "node_modules/@github1s/vscode-web": { - "version": "0.30.2", - "resolved": "https://registry.npmjs.org/@github1s/vscode-web/-/vscode-web-0.30.2.tgz", - "integrity": "sha512-k4xjaMbGK1dhn6EmZSbm9o68i/8yqClC3T24EhIcb6Drago+smgpx+NdkLo9aGDrfjvF5bJZlfWV+XcVZqfa5g==", + "version": "0.30.3", + "resolved": "https://registry.npmjs.org/@github1s/vscode-web/-/vscode-web-0.30.3.tgz", + "integrity": "sha512-wK6fvcCTSPK8EIQZA+o4IGBQLwudOA/TgG+0C+1sI4TvGG2lu/wCi2RNMIjJZpU4hzcaR0T3RQi7PtnBdiGQmA==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index 2d6460f37..2199c35e2 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "license": "ISC", "devDependencies": { "@cloudflare/workers-types": "^5.20260910.1", - "@github1s/vscode-web": "^0.30.2", + "@github1s/vscode-web": "^0.30.3", "@types/node": "^22.10.5", "chokidar": "^4.0.3", "clean-css": "^5.3.3", diff --git a/vscode-web/package-lock.json b/vscode-web/package-lock.json index 9faca8ef3..2c72de1cc 100644 --- a/vscode-web/package-lock.json +++ b/vscode-web/package-lock.json @@ -1,12 +1,12 @@ { "name": "@github1s/vscode-web", - "version": "0.30.2", + "version": "0.30.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@github1s/vscode-web", - "version": "0.30.2", + "version": "0.30.3", "license": "MIT", "devDependencies": { "chokidar": "^4.0.3", @@ -90,9 +90,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/vscode-web/package.json b/vscode-web/package.json index e64cf694b..7a0ea0a8d 100644 --- a/vscode-web/package.json +++ b/vscode-web/package.json @@ -1,6 +1,6 @@ { "name": "@github1s/vscode-web", - "version": "0.30.2", + "version": "0.30.3", "description": "VS Code web for GitHub1s", "author": "github1s", "license": "MIT", diff --git a/vscode-web/scripts/.patch b/vscode-web/scripts/.patch index f4246ba24..73e506184 100644 --- a/vscode-web/scripts/.patch +++ b/vscode-web/scripts/.patch @@ -7,5 +7,6 @@ "src/vs/workbench/services/label/common/labelService.ts": "9e4e2aafeabc04ccf183b8f2f4ce8a4d39f240831e4005b06149684458caa8d4", "src/vs/workbench/services/textfile/browser/textFileService.ts": "886566605815367d3866be243994829729fd0b85c04ab7450b9de22ee93053eb", "src/vs/base/common/network.ts": "d264af1ba5dbbfaf950c5da336100fb1242802eab92ed3b4692d3c10282b2dee", - "src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts": "f77c8e0d95ff5e0b15f0ea5135f3b454ee6b12dbf07c793ae3e053ead954e47a" + "src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts": "f77c8e0d95ff5e0b15f0ea5135f3b454ee6b12dbf07c793ae3e053ead954e47a", + "src/vs/workbench/contrib/scm/browser/scm.contribution.ts": "82d17f9575c13d810e9f65ef5e47c68ff007c0155db9eaa0c434d67e67cd63b8" } \ No newline at end of file diff --git a/vscode-web/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/vscode-web/src/vs/workbench/contrib/scm/browser/scm.contribution.ts new file mode 100644 index 000000000..e5b34a129 --- /dev/null +++ b/vscode-web/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -0,0 +1,712 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize, localize2 } from '../../../../nls.js'; +import { Registry } from '../../../../platform/registry/common/platform.js'; +import { IWorkbenchContributionsRegistry, registerWorkbenchContribution2, Extensions as WorkbenchExtensions, WorkbenchPhase } from '../../../common/contributions.js'; +import { VIEWLET_ID, ISCMService, VIEW_PANE_ID, ISCMProvider, ISCMViewService, REPOSITORIES_VIEW_PANE_ID, HISTORY_VIEW_PANE_ID } from '../common/scm.js'; +import { KeyMod, KeyCode } from '../../../../base/common/keyCodes.js'; +import { MenuRegistry, MenuId, registerAction2, Action2 } from '../../../../platform/actions/common/actions.js'; +import { SCMActiveResourceContextKeyController, SCMActiveRepositoryController } from './activity.js'; +import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from '../../../../platform/configuration/common/configurationRegistry.js'; +import { IContextKeyService, ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; +import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js'; +import { KeybindingsRegistry, KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; +import { IViewContainersRegistry, ViewContainerLocation, Extensions as ViewContainerExtensions, IViewsRegistry } from '../../../common/views.js'; +import { SCMViewPaneContainer } from './scmViewPaneContainer.js'; +import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; +import { ModesRegistry } from '../../../../editor/common/languages/modesRegistry.js'; +import { Codicon } from '../../../../base/common/codicons.js'; +import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; +import { ContextKeys, SCMViewPane } from './scmViewPane.js'; +import { RepositoryPicker } from './scmViewService.js'; +import { SCMRepositoriesViewPane } from './scmRepositoriesViewPane.js'; +import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +import { Context as SuggestContext } from '../../../../editor/contrib/suggest/browser/suggest.js'; +import { InlineCompletionContextKeys } from '../../../../editor/contrib/inlineCompletions/browser/controller/inlineCompletionContextKeys.js'; +import { MANAGE_TRUST_COMMAND_ID, WorkspaceTrustContext } from '../../workspace/common/workspace.js'; +import { getActiveElement, isActiveElement } from '../../../../base/browser/dom.js'; +import { SCMWorkingSetController } from './workingSet.js'; +import { IViewsService } from '../../../services/views/common/viewsService.js'; +import { IListService, WorkbenchList } from '../../../../platform/list/browser/listService.js'; +import { isSCMRepository } from './util.js'; +import { SCMHistoryViewPane } from './scmHistoryViewPane.js'; +import { RemoteNameContext, ResourceContextKey } from '../../../common/contextkeys.js'; +import { AccessibleViewRegistry } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js'; +import { SCMAccessibilityHelp } from './scmAccessibilityHelp.js'; +import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; +import { SCMHistoryItemContextContribution } from './scmHistoryChatContext.js'; +import { ChatContextKeys } from '../../chat/common/actions/chatContextKeys.js'; +import { CHAT_SETUP_SUPPORT_ANONYMOUS_ACTION_ID } from '../../chat/browser/actions/chatActions.js'; +import { SCMInputContextKeys } from './scmInput.js'; +import product from '../../../../platform/product/common/product.js'; + +ModesRegistry.registerLanguage({ + id: 'scminput', + extensions: [], + aliases: [], // hide from language selector + mimetypes: ['text/x-scm-input'] +}); + +const sourceControlViewIcon = registerIcon('source-control-view-icon', Codicon.sourceControl, localize('sourceControlViewIcon', 'View icon of the Source Control view.')); + +const viewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ + id: VIEWLET_ID, + title: localize2('source control', 'Source Control'), + ctorDescriptor: new SyncDescriptor(SCMViewPaneContainer), + storageId: 'workbench.scm.views.state', + icon: sourceControlViewIcon, + alwaysUseContainerInfo: true, + order: 2, + hideIfEmpty: true, +}, ViewContainerLocation.Sidebar, { doNotRegisterOpenCommand: true }); + +const viewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); +const containerTitle = localize('source control view', "Source Control"); + +viewsRegistry.registerViewWelcomeContent(VIEW_PANE_ID, { + content: localize('no open repo', "No source control providers registered."), + when: 'default' +}); + +viewsRegistry.registerViewWelcomeContent(VIEW_PANE_ID, { + content: localize('no open repo in an untrusted workspace', "None of the registered source control providers work in Restricted Mode."), + when: ContextKeyExpr.and(ContextKeyExpr.equals('scm.providerCount', 0), WorkspaceTrustContext.IsEnabled, WorkspaceTrustContext.IsTrusted.toNegated()) +}); + +viewsRegistry.registerViewWelcomeContent(VIEW_PANE_ID, { + content: `[${localize('manageWorkspaceTrustAction', "Manage Workspace Trust")}](command:${MANAGE_TRUST_COMMAND_ID})`, + when: ContextKeyExpr.and(ContextKeyExpr.equals('scm.providerCount', 0), WorkspaceTrustContext.IsEnabled, WorkspaceTrustContext.IsTrusted.toNegated()) +}); + +viewsRegistry.registerViewWelcomeContent(HISTORY_VIEW_PANE_ID, { + content: localize('no history items', "The selected source control provider does not have any source control history items."), + when: ContextKeys.SCMHistoryItemCount.isEqualTo(0) +}); + +viewsRegistry.registerViews([{ + id: REPOSITORIES_VIEW_PANE_ID, + containerTitle, + name: localize2('scmRepositories', "Repositories"), + singleViewPaneContainerTitle: localize('source control repositories', "Source Control Repositories"), + ctorDescriptor: new SyncDescriptor(SCMRepositoriesViewPane), + canToggleVisibility: true, + hideByDefault: true, + canMoveView: true, + weight: 20, + order: 0, + when: ContextKeyExpr.and(ContextKeyExpr.has('scm.providerCount'), ContextKeyExpr.notEquals('scm.providerCount', 0)), + // readonly when = ContextKeyExpr.or(ContextKeyExpr.equals('config.scm.alwaysShowProviders', true), ContextKeyExpr.and(ContextKeyExpr.notEquals('scm.providerCount', 0), ContextKeyExpr.notEquals('scm.providerCount', 1))); + containerIcon: sourceControlViewIcon +}], viewContainer); + +viewsRegistry.registerViews([{ + id: VIEW_PANE_ID, + containerTitle, + name: localize2('scmChanges', 'Changes'), + singleViewPaneContainerTitle: containerTitle, + ctorDescriptor: new SyncDescriptor(SCMViewPane), + canToggleVisibility: true, + canMoveView: true, + weight: 40, + order: 1, + containerIcon: sourceControlViewIcon, + openCommandActionDescriptor: { + id: viewContainer.id, + mnemonicTitle: localize({ key: 'miViewSCM', comment: ['&& denotes a mnemonic'] }, "Source &&Control"), + keybindings: { + primary: 0, + win: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyG }, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyG }, + mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KeyG }, + }, + order: 2, + } +}], viewContainer); + +viewsRegistry.registerViews([{ + id: HISTORY_VIEW_PANE_ID, + containerTitle, + name: localize2('scmGraph', "Graph"), + singleViewPaneContainerTitle: localize('source control graph', "Source Control Graph"), + ctorDescriptor: new SyncDescriptor(SCMHistoryViewPane), + canToggleVisibility: true, + canMoveView: true, + /* below codes are changed by github1s */ + // default collapsed history view + collapsed: true, + /* above codes are changed by github1s */ + weight: 40, + order: 2, + when: ContextKeyExpr.and( + ContextKeyExpr.has('scm.historyProviderCount'), + ContextKeyExpr.notEquals('scm.historyProviderCount', 0), + ), + containerIcon: sourceControlViewIcon +}], viewContainer); + +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(SCMActiveRepositoryController, LifecyclePhase.Restored); + +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(SCMActiveResourceContextKeyController, LifecyclePhase.Restored); + +registerWorkbenchContribution2( + SCMWorkingSetController.ID, + SCMWorkingSetController, + WorkbenchPhase.AfterRestored +); + +registerWorkbenchContribution2( + SCMHistoryItemContextContribution.ID, + SCMHistoryItemContextContribution, + WorkbenchPhase.AfterRestored +); + +Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ + id: 'scm', + order: 5, + title: localize('scmConfigurationTitle', "Source Control"), + type: 'object', + scope: ConfigurationScope.RESOURCE, + properties: { + 'scm.diffDecorations': { + type: 'string', + enum: ['all', 'gutter', 'overview', 'minimap', 'none'], + enumDescriptions: [ + localize('scm.diffDecorations.all', "Show the diff decorations in all available locations."), + localize('scm.diffDecorations.gutter', "Show the diff decorations only in the editor gutter."), + localize('scm.diffDecorations.overviewRuler', "Show the diff decorations only in the overview ruler."), + localize('scm.diffDecorations.minimap', "Show the diff decorations only in the minimap."), + localize('scm.diffDecorations.none', "Do not show the diff decorations.") + ], + default: 'all', + description: localize('diffDecorations', "Controls diff decorations in the editor.") + }, + 'scm.diffDecorationsGutterWidth': { + type: 'number', + enum: [1, 2, 3, 4, 5], + default: 3, + description: localize('diffGutterWidth', "Controls the width(px) of diff decorations in gutter (added & modified).") + }, + 'scm.diffDecorationsGutterVisibility': { + type: 'string', + enum: ['always', 'hover'], + enumDescriptions: [ + localize('scm.diffDecorationsGutterVisibility.always', "Show the diff decorator in the gutter at all times."), + localize('scm.diffDecorationsGutterVisibility.hover', "Show the diff decorator in the gutter only on hover.") + ], + description: localize('scm.diffDecorationsGutterVisibility', "Controls the visibility of the Source Control diff decorator in the gutter."), + default: 'always' + }, + 'scm.diffDecorationsGutterAction': { + type: 'string', + enum: ['diff', 'none'], + enumDescriptions: [ + localize('scm.diffDecorationsGutterAction.diff', "Show the inline diff Peek view on click."), + localize('scm.diffDecorationsGutterAction.none', "Do nothing.") + ], + description: localize('scm.diffDecorationsGutterAction', "Controls the behavior of Source Control diff gutter decorations."), + default: 'diff' + }, + 'scm.diffDecorationsGutterPattern': { + type: 'object', + description: localize('diffGutterPattern', "Controls whether a pattern is used for the diff decorations in gutter."), + additionalProperties: false, + properties: { + 'added': { + type: 'boolean', + description: localize('diffGutterPatternAdded', "Use pattern for the diff decorations in gutter for added lines."), + }, + 'modified': { + type: 'boolean', + description: localize('diffGutterPatternModifed', "Use pattern for the diff decorations in gutter for modified lines."), + }, + }, + default: { + 'added': false, + 'modified': true + } + }, + 'scm.diffDecorationsIgnoreTrimWhitespace': { + type: 'string', + enum: ['true', 'false', 'inherit'], + enumDescriptions: [ + localize('scm.diffDecorationsIgnoreTrimWhitespace.true', "Ignore leading and trailing whitespace."), + localize('scm.diffDecorationsIgnoreTrimWhitespace.false', "Do not ignore leading and trailing whitespace."), + localize('scm.diffDecorationsIgnoreTrimWhitespace.inherit', "Inherit from `diffEditor.ignoreTrimWhitespace`.") + ], + description: localize('diffDecorationsIgnoreTrimWhitespace', "Controls whether leading and trailing whitespace is ignored in Source Control diff gutter decorations."), + default: 'false' + }, + 'scm.alwaysShowActions': { + type: 'boolean', + description: localize('alwaysShowActions', "Controls whether inline actions are always visible in the Source Control view."), + default: false + }, + 'scm.countBadge': { + type: 'string', + enum: ['all', 'focused', 'off'], + enumDescriptions: [ + localize('scm.countBadge.all', "Show the sum of all Source Control Provider count badges."), + localize('scm.countBadge.focused', "Show the count badge of the focused Source Control Provider."), + localize('scm.countBadge.off', "Disable the Source Control count badge.") + ], + description: localize('scm.countBadge', "Controls the count badge on the Source Control icon on the Activity Bar."), + default: 'all' + }, + 'scm.providerCountBadge': { + type: 'string', + enum: ['hidden', 'auto', 'visible'], + enumDescriptions: [ + localize('scm.providerCountBadge.hidden', "Hide Source Control Provider count badges."), + localize('scm.providerCountBadge.auto', "Only show count badge for Source Control Provider when non-zero."), + localize('scm.providerCountBadge.visible', "Show Source Control Provider count badges.") + ], + markdownDescription: localize('scm.providerCountBadge', "Controls the count badges on Source Control Provider headers. These headers appear in the Source Control view when there is more than one provider or when the {0} setting is enabled, and in the Source Control Repositories view.", '\`#scm.alwaysShowRepositories#\`'), + default: 'hidden' + }, + 'scm.defaultViewMode': { + type: 'string', + enum: ['tree', 'list'], + enumDescriptions: [ + localize('scm.defaultViewMode.tree', "Show the repository changes as a tree."), + localize('scm.defaultViewMode.list', "Show the repository changes as a list.") + ], + description: localize('scm.defaultViewMode', "Controls the default Source Control repository view mode."), + default: 'list' + }, + 'scm.defaultViewSortKey': { + type: 'string', + enum: ['name', 'path', 'status'], + enumDescriptions: [ + localize('scm.defaultViewSortKey.name', "Sort the repository changes by file name."), + localize('scm.defaultViewSortKey.path', "Sort the repository changes by path."), + localize('scm.defaultViewSortKey.status', "Sort the repository changes by Source Control status.") + ], + description: localize('scm.defaultViewSortKey', "Controls the default Source Control repository changes sort order when viewed as a list."), + default: 'path' + }, + 'scm.autoReveal': { + type: 'boolean', + description: localize('autoReveal', "Controls whether the Source Control view should automatically reveal and select files when opening them."), + default: true + }, + 'scm.inputFontFamily': { + type: 'string', + markdownDescription: localize('inputFontFamily', "Controls the font for the input message. Use `default` for the workbench user interface font family, `editor` for the `#editor.fontFamily#`'s value, or a custom font family."), + default: 'default' + }, + 'scm.inputFontSize': { + type: 'number', + markdownDescription: localize('inputFontSize', "Controls the font size for the input message in pixels."), + default: 13 + }, + 'scm.inputMaxLineCount': { + type: 'number', + markdownDescription: localize('inputMaxLines', "Controls the maximum number of lines that the input will auto-grow to."), + minimum: 1, + maximum: 50, + default: 10 + }, + 'scm.inputMinLineCount': { + type: 'number', + markdownDescription: localize('inputMinLines', "Controls the minimum number of lines that the input will auto-grow from."), + minimum: 1, + maximum: 50, + default: 1 + }, + 'scm.alwaysShowRepositories': { + type: 'boolean', + markdownDescription: localize('alwaysShowRepository', "Controls whether repositories should always be visible in the Source Control view."), + default: false + }, + 'scm.repositories.sortOrder': { + type: 'string', + enum: ['discovery time', 'name', 'path'], + enumDescriptions: [ + localize('scm.repositoriesSortOrder.discoveryTime', "Repositories in the Source Control Repositories view are sorted by discovery time. Repositories in the Source Control view are sorted in the order that they were selected."), + localize('scm.repositoriesSortOrder.name', "Repositories in the Source Control Repositories and Source Control views are sorted by repository name."), + localize('scm.repositoriesSortOrder.path', "Repositories in the Source Control Repositories and Source Control views are sorted by repository path.") + ], + description: localize('repositoriesSortOrder', "Controls the sort order of the repositories in the source control repositories view."), + default: 'discovery time' + }, + 'scm.repositories.visible': { + type: 'number', + description: localize('providersVisible', "Controls how many repositories are visible in the Source Control Repositories section. Set to 0, to be able to manually resize the view."), + default: 10 + }, + 'scm.repositories.selectionMode': { + type: 'string', + enum: ['multiple', 'single'], + enumDescriptions: [ + localize('scm.repositories.selectionMode.multiple', "Multiple repositories can be selected at the same time."), + localize('scm.repositories.selectionMode.single', "Only one repository can be selected at a time.") + ], + description: localize('scm.repositories.selectionMode', "Controls the selection mode of the repositories in the Source Control Repositories view."), + default: 'multiple' + }, + 'scm.repositories.explorer': { + type: 'boolean', + markdownDescription: localize('scm.repositories.explorer', "Controls whether to show repository artifacts in the Source Control Repositories view. This feature is experimental and only works when {0} is set to `{1}`.", '\`#scm.repositories.selectionMode#\`', 'single'), + default: false, + tags: ['experimental'] + }, + 'scm.showActionButton': { + type: 'boolean', + markdownDescription: localize('showActionButton', "Controls whether an action button can be shown in the Source Control view."), + default: true + }, + 'scm.showInputActionButton': { + type: 'boolean', + markdownDescription: localize('showInputActionButton', "Controls whether an action button can be shown in the Source Control input."), + default: true + }, + 'scm.workingSets.enabled': { + type: 'boolean', + description: localize('scm.workingSets.enabled', "Controls whether to store editor working sets when switching between source control history item groups."), + default: false + }, + 'scm.workingSets.default': { + type: 'string', + enum: ['empty', 'current'], + enumDescriptions: [ + localize('scm.workingSets.default.empty', "Use an empty working set when switching to a source control history item group that does not have a working set."), + localize('scm.workingSets.default.current', "Use the current working set when switching to a source control history item group that does not have a working set.") + ], + description: localize('scm.workingSets.default', "Controls the default working set to use when switching to a source control history item group that does not have a working set."), + default: 'current' + }, + 'scm.compactFolders': { + type: 'boolean', + description: localize('scm.compactFolders', "Controls whether the Source Control view should render folders in a compact form. In such a form, single child folders will be compressed in a combined tree element."), + default: true + }, + 'scm.graph.pageOnScroll': { + type: 'boolean', + description: localize('scm.graph.pageOnScroll', "Controls whether the Source Control Graph view will load the next page of items when you scroll to the end of the list."), + default: true + }, + 'scm.graph.pageSize': { + type: 'number', + description: localize('scm.graph.pageSize', "The number of items to show in the Source Control Graph view by default and when loading more items."), + minimum: 1, + maximum: 1000, + default: 50 + }, + 'scm.graph.badges': { + type: 'string', + enum: ['all', 'filter'], + enumDescriptions: [ + localize('scm.graph.badges.all', "Show badges of all history item groups in the Source Control Graph view."), + localize('scm.graph.badges.filter', "Show only the badges of history item groups used as a filter in the Source Control Graph view.") + ], + description: localize('scm.graph.badges', "Controls which badges are shown in the Source Control Graph view. The badges are shown on the right side of the graph indicating the names of history item groups."), + default: 'filter' + }, + 'scm.graph.showIncomingChanges': { + type: 'boolean', + description: localize('scm.graph.showIncomingChanges', "Controls whether to show incoming changes in the Source Control Graph view."), + default: true + }, + 'scm.graph.showOutgoingChanges': { + type: 'boolean', + description: localize('scm.graph.showOutgoingChanges', "Controls whether to show outgoing changes in the Source Control Graph view."), + default: true + } + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'scm.acceptInput', + metadata: { description: localize('scm accept', "Source Control: Accept Input"), args: [] }, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.has('scmRepository'), + primary: KeyMod.CtrlCmd | KeyCode.Enter, + handler: accessor => { + const contextKeyService = accessor.get(IContextKeyService); + const context = contextKeyService.getContext(getActiveElement()); + const repositoryId = context.getValue('scmRepository'); + + if (!repositoryId) { + return Promise.resolve(null); + } + + const scmService = accessor.get(ISCMService); + const repository = scmService.getRepository(repositoryId); + + if (!repository?.provider.acceptInputCommand) { + return Promise.resolve(null); + } + + const id = repository.provider.acceptInputCommand.id; + const args = repository.provider.acceptInputCommand.arguments; + const commandService = accessor.get(ICommandService); + + return commandService.executeCommand(id, ...(args || [])); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'scm.clearValidation', + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and( + ContextKeyExpr.has('scmRepository'), + SCMInputContextKeys.SCMInputHasValidationMessage), + primary: KeyCode.Escape, + handler: async (accessor) => { + const scmViewService = accessor.get(ISCMViewService); + scmViewService.activeRepository.get()?.repository.input.clearValidation(); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'scm.clearInput', + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and( + ContextKeyExpr.has('scmRepository'), + SuggestContext.Visible.toNegated(), + InlineCompletionContextKeys.inlineSuggestionVisible.toNegated(), + SCMInputContextKeys.SCMInputHasValidationMessage.toNegated(), + EditorContextKeys.hasNonEmptySelection.toNegated()), + primary: KeyCode.Escape, + handler: async (accessor) => { + const scmService = accessor.get(ISCMService); + const contextKeyService = accessor.get(IContextKeyService); + + const context = contextKeyService.getContext(getActiveElement()); + const repositoryId = context.getValue('scmRepository'); + const repository = repositoryId ? scmService.getRepository(repositoryId) : undefined; + repository?.input.setValue('', true); + } +}); + +const viewNextCommitCommand = { + description: { description: localize('scm view next commit', "Source Control: View Next Commit"), args: [] }, + weight: KeybindingWeight.WorkbenchContrib, + handler: (accessor: ServicesAccessor) => { + const contextKeyService = accessor.get(IContextKeyService); + const scmService = accessor.get(ISCMService); + const context = contextKeyService.getContext(getActiveElement()); + const repositoryId = context.getValue('scmRepository'); + const repository = repositoryId ? scmService.getRepository(repositoryId) : undefined; + repository?.input.showNextHistoryValue(); + } +}; + +const viewPreviousCommitCommand = { + description: { description: localize('scm view previous commit', "Source Control: View Previous Commit"), args: [] }, + weight: KeybindingWeight.WorkbenchContrib, + handler: (accessor: ServicesAccessor) => { + const contextKeyService = accessor.get(IContextKeyService); + const scmService = accessor.get(ISCMService); + const context = contextKeyService.getContext(getActiveElement()); + const repositoryId = context.getValue('scmRepository'); + const repository = repositoryId ? scmService.getRepository(repositoryId) : undefined; + repository?.input.showPreviousHistoryValue(); + } +}; + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + ...viewNextCommitCommand, + id: 'scm.viewNextCommit', + when: ContextKeyExpr.and(ContextKeyExpr.has('scmRepository'), ContextKeyExpr.has('scmInputIsInLastPosition'), SuggestContext.Visible.toNegated()), + primary: KeyCode.DownArrow +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + ...viewPreviousCommitCommand, + id: 'scm.viewPreviousCommit', + when: ContextKeyExpr.and(ContextKeyExpr.has('scmRepository'), ContextKeyExpr.has('scmInputIsInFirstPosition'), SuggestContext.Visible.toNegated()), + primary: KeyCode.UpArrow +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + ...viewNextCommitCommand, + id: 'scm.forceViewNextCommit', + when: ContextKeyExpr.has('scmRepository'), + primary: KeyMod.Alt | KeyCode.DownArrow +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + ...viewPreviousCommitCommand, + id: 'scm.forceViewPreviousCommit', + when: ContextKeyExpr.has('scmRepository'), + primary: KeyMod.Alt | KeyCode.UpArrow +}); + +CommandsRegistry.registerCommand('scm.openInIntegratedTerminal', async (accessor, ...providers: ISCMProvider[]) => { + if (!providers || providers.length === 0) { + return; + } + + const commandService = accessor.get(ICommandService); + const listService = accessor.get(IListService); + + let provider = providers.length === 1 ? providers[0] : undefined; + + if (!provider) { + const list = listService.lastFocusedList; + const element = list?.getHTMLElement(); + + if (list instanceof WorkbenchList && element && isActiveElement(element)) { + const [index] = list.getFocus(); + const focusedElement = list.element(index); + + // Source Control Repositories + if (isSCMRepository(focusedElement)) { + provider = focusedElement.provider; + } + } + } + + if (!provider?.rootUri) { + return; + } + + await commandService.executeCommand('openInIntegratedTerminal', provider.rootUri); +}); + +CommandsRegistry.registerCommand('scm.openInTerminal', async (accessor, provider: ISCMProvider) => { + if (!provider || !provider.rootUri) { + return; + } + + const commandService = accessor.get(ICommandService); + await commandService.executeCommand('openInTerminal', provider.rootUri); +}); + +CommandsRegistry.registerCommand('scm.setActiveProvider', async (accessor) => { + const instantiationService = accessor.get(IInstantiationService); + const scmViewService = accessor.get(ISCMViewService); + + const placeHolder = localize('scmActiveRepositoryPlaceHolder', "Select the active repository, type to filter all repositories"); + const autoQuickItemDescription = localize('scmActiveRepositoryAutoDescription', "The active repository is updated based on active editor"); + const repositoryPicker = instantiationService.createInstance(RepositoryPicker, placeHolder, autoQuickItemDescription); + + const result = await repositoryPicker.pickRepository(); + if (result?.repository) { + const repository = result.repository !== 'auto' ? result.repository : undefined; + scmViewService.pinActiveRepository(repository); + } +}); + +MenuRegistry.appendMenuItem(MenuId.SCMSourceControl, { + group: '99_terminal', + command: { + id: 'scm.openInTerminal', + title: localize('open in external terminal', "Open in External Terminal") + }, + when: ContextKeyExpr.and( + RemoteNameContext.isEqualTo(''), + ContextKeyExpr.equals('scmProviderHasRootUri', true), + ContextKeyExpr.or( + ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'external'), + ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'both'))) +}); + +MenuRegistry.appendMenuItem(MenuId.SCMSourceControl, { + group: '99_terminal', + command: { + id: 'scm.openInIntegratedTerminal', + title: localize('open in integrated terminal', "Open in Integrated Terminal") + }, + when: ContextKeyExpr.and( + ContextKeyExpr.equals('scmProviderHasRootUri', true), + ContextKeyExpr.or( + ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'integrated'), + ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'both'))) +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.scm.action.focusPreviousInput', + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeys.RepositoryVisibilityCount.notEqualsTo(0), + handler: async accessor => { + const viewsService = accessor.get(IViewsService); + const scmView = await viewsService.openView(VIEW_PANE_ID); + if (scmView) { + scmView.focusPreviousInput(); + } + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.scm.action.focusNextInput', + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeys.RepositoryVisibilityCount.notEqualsTo(0), + handler: async accessor => { + const viewsService = accessor.get(IViewsService); + const scmView = await viewsService.openView(VIEW_PANE_ID); + if (scmView) { + scmView.focusNextInput(); + } + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.scm.action.focusPreviousResourceGroup', + weight: KeybindingWeight.WorkbenchContrib, + handler: async accessor => { + const viewsService = accessor.get(IViewsService); + const scmView = await viewsService.openView(VIEW_PANE_ID); + if (scmView) { + scmView.focusPreviousResourceGroup(); + } + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.scm.action.focusNextResourceGroup', + weight: KeybindingWeight.WorkbenchContrib, + handler: async accessor => { + const viewsService = accessor.get(IViewsService); + const scmView = await viewsService.openView(VIEW_PANE_ID); + if (scmView) { + scmView.focusNextResourceGroup(); + } + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'scm.editor.triggerSetup', + title: localize('scmEditorResolveMergeConflict', "Resolve Conflicts with AI"), + icon: Codicon.chatSparkle, + f1: false, + menu: { + id: MenuId.EditorContent, + when: ContextKeyExpr.and( + ChatContextKeys.Setup.hidden.negate(), + ChatContextKeys.Setup.disabledInWorkspace.negate(), + ChatContextKeys.Setup.completed.negate(), + ContextKeyExpr.in(ResourceContextKey.Resource.key, 'git.mergeChanges'), + ContextKeyExpr.equals('git.activeResourceHasMergeConflicts', true) + ) + } + }); + } + + override async run(accessor: ServicesAccessor, ...args: unknown[]): Promise { + const commandService = accessor.get(ICommandService); + + const result = await commandService.executeCommand(CHAT_SETUP_SUPPORT_ANONYMOUS_ACTION_ID); + if (!result) { + return; + } + + const command = product.defaultChatAgent?.resolveMergeConflictsCommand; + if (!command) { + return; + } + + await commandService.executeCommand(command, ...args); + } +}); + +AccessibleViewRegistry.register(new SCMAccessibilityHelp());