diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index fcf9ad4a40d1b..35538d67cf51d 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -194,10 +194,6 @@ "name": "vs/workbench/contrib/searchEditor", "project": "vscode-workbench" }, - { - "name": "vs/workbench/contrib/share", - "project": "vscode-workbench" - }, { "name": "vs/workbench/contrib/snippets", "project": "vscode-workbench" diff --git a/extensions/github/package.json b/extensions/github/package.json index 8b78f0e27559c..68cbd72cbfa91 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -38,13 +38,16 @@ }, { "command": "github.copyVscodeDevLink", - "enablement": "github.hasGitHubRepo && resourceScheme != untitled && remoteName != 'codespaces'", "title": "Copy vscode.dev Link" }, { "command": "github.copyVscodeDevLinkFile", "title": "Copy vscode.dev Link" }, + { + "command": "github.copyVscodeDevLinkWithoutRange", + "title": "Copy vscode.dev Link" + }, { "command": "github.openOnVscodeDev", "title": "Open in vscode.dev", @@ -74,24 +77,56 @@ "command": "github.copyVscodeDevLinkFile", "when": "false" }, + { + "command": "github.copyVscodeDevLinkWithoutRange", + "when": "false" + }, { "command": "github.openOnVscodeDev", "when": "false" } ], - "share": [ + "file/share": [ + { + "command": "github.copyVscodeDevLinkFile", + "when": "github.hasGitHubRepo && remoteName != 'codespaces'", + "group": "0_vscode@0" + } + ], + "editor/context/share": [ + { + "command": "github.copyVscodeDevLink", + "when": "github.hasGitHubRepo && resourceScheme != untitled && remoteName != 'codespaces'", + "group": "0_vscode@0" + } + ], + "explorer/context/share": [ + { + "command": "github.copyVscodeDevLinkWithoutRange", + "when": "github.hasGitHubRepo && resourceScheme != untitled && remoteName != 'codespaces'", + "group": "0_vscode@0" + } + ], + "editor/lineNumber/context": [ + { + "command": "github.copyVscodeDevLink", + "when": "github.hasGitHubRepo && resourceScheme != untitled && activeEditor == workbench.editors.files.textFileEditor && config.editor.lineNumbers == on && remoteName != 'codespaces'", + "group": "1_cutcopypaste@2" + }, { "command": "github.copyVscodeDevLink", - "title": "Copy vscode.dev Link" + "when": "github.hasGitHubRepo && resourceScheme != untitled && activeEditor == workbench.editor.notebook && remoteName != 'codespaces'", + "group": "1_cutcopypaste@2" } ], - "file/share": [ + "editor/title/context/share": [ { - "command": "github.copyVscodeDevLinkFile", - "when": "github.hasGitHubRepo && remoteName != 'codespaces'", + "command": "github.copyVscodeDevLinkWithoutRange", + "when": "github.hasGitHubRepo && resourceScheme != untitled && remoteName != 'codespaces'", "group": "0_vscode@0" } ] + }, "configuration": [ { diff --git a/extensions/github/src/commands.ts b/extensions/github/src/commands.ts index 0f4348e376c32..7158759c672f5 100644 --- a/extensions/github/src/commands.ts +++ b/extensions/github/src/commands.ts @@ -45,10 +45,7 @@ export function registerCommands(gitAPI: GitAPI): vscode.Disposable { } })); - disposables.add(vscode.commands.registerCommand('github.copyVscodeDevLink', async (context: LinkContext, ranges?: vscode.Range[]) => { - if (Array.isArray(ranges) && ranges.every((range) => 'start' in range && 'end' in range) && context instanceof vscode.Uri) { - context = { uri: context, ranges }; - } + disposables.add(vscode.commands.registerCommand('github.copyVscodeDevLink', async (context: LinkContext) => { return copyVscodeDevLink(gitAPI, true, context); })); @@ -56,6 +53,10 @@ export function registerCommands(gitAPI: GitAPI): vscode.Disposable { return copyVscodeDevLink(gitAPI, false, context); })); + disposables.add(vscode.commands.registerCommand('github.copyVscodeDevLinkWithoutRange', async (context: LinkContext) => { + return copyVscodeDevLink(gitAPI, true, context, false); + })); + disposables.add(vscode.commands.registerCommand('github.openOnVscodeDev', async () => { return openVscodeDevLink(gitAPI); })); diff --git a/extensions/github/src/links.ts b/extensions/github/src/links.ts index ce6bfdb995095..5cbdf2e78bccf 100644 --- a/extensions/github/src/links.ts +++ b/extensions/github/src/links.ts @@ -40,41 +40,27 @@ interface INotebookPosition { range: vscode.Range | undefined; } -interface EditorContext { - uri: vscode.Uri; - ranges: vscode.Range[]; -} - interface EditorLineNumberContext { uri: vscode.Uri; lineNumber: number; } +export type LinkContext = vscode.Uri | EditorLineNumberContext | undefined; -interface ScmResourceContext { - resourceUri: vscode.Uri; -} - -export type LinkContext = vscode.Uri | EditorContext | EditorLineNumberContext | undefined | ScmResourceContext; - -function extractContext(context: LinkContext): { fileUri: vscode.Uri | undefined; ranges: vscode.Range[] | undefined } { +function extractContext(context: LinkContext): { fileUri: vscode.Uri | undefined; lineNumber: number | undefined } { if (context instanceof vscode.Uri) { - return { fileUri: context, ranges: undefined }; - } else if (context !== undefined && 'ranges' in context && 'uri' in context) { - return { fileUri: context.uri, ranges: context.ranges }; + return { fileUri: context, lineNumber: undefined }; } else if (context !== undefined && 'lineNumber' in context && 'uri' in context) { - return { fileUri: context.uri, ranges: [new vscode.Range(context.lineNumber - 1, 0, context.lineNumber - 1, 1)] }; - } else if (context !== undefined && 'resourceUri' in context) { - return { fileUri: context.resourceUri, ranges: undefined }; + return { fileUri: context.uri, lineNumber: context.lineNumber }; } else { - return { fileUri: undefined, ranges: undefined }; + return { fileUri: undefined, lineNumber: undefined }; } } function getFileAndPosition(context: LinkContext): IFilePosition | INotebookPosition | undefined { let range: vscode.Range | undefined; - const { fileUri, ranges: selections } = extractContext(context); - const uri = fileUri; + const { fileUri, lineNumber } = extractContext(context); + const uri = fileUri ?? vscode.window.activeTextEditor?.document.uri; if (uri) { if (uri.scheme === 'vscode-notebook-cell' && vscode.window.activeNotebookEditor?.notebook.uri.fsPath === uri.fsPath) { @@ -83,11 +69,11 @@ function getFileAndPosition(context: LinkContext): IFilePosition | INotebookPosi const cell = vscode.window.activeNotebookEditor.notebook.getCells().find(cell => cell.document.uri.fragment === uri?.fragment); const cellIndex = cell?.index ?? vscode.window.activeNotebookEditor.selection.start; - const range = selections?.[0]; + const range = getRangeOrSelection(lineNumber); return { type: LinkType.Notebook, uri, cellIndex, range }; } else { // the active editor is a text editor - range = selections?.[0]; + range = getRangeOrSelection(lineNumber); return { type: LinkType.File, uri, range }; } } @@ -100,6 +86,12 @@ function getFileAndPosition(context: LinkContext): IFilePosition | INotebookPosi return undefined; } +function getRangeOrSelection(lineNumber: number | undefined) { + return lineNumber !== undefined && (!vscode.window.activeTextEditor || vscode.window.activeTextEditor.selection.isEmpty || !vscode.window.activeTextEditor.selection.contains(new vscode.Position(lineNumber - 1, 0))) + ? new vscode.Range(lineNumber - 1, 0, lineNumber - 1, 1) + : vscode.window.activeTextEditor?.selection; +} + function rangeString(range: vscode.Range | undefined) { if (!range) { return ''; @@ -130,7 +122,10 @@ export function notebookCellRangeString(index: number | undefined, range: vscode export function getLink(gitAPI: GitAPI, useSelection: boolean, hostPrefix?: string, linkType: 'permalink' | 'headlink' = 'permalink', context?: LinkContext, useRange?: boolean): string | undefined { hostPrefix = hostPrefix ?? 'https://github.com'; const fileAndPosition = getFileAndPosition(context); - const uri = fileAndPosition?.uri; + if (!fileAndPosition) { + return; + } + const uri = fileAndPosition.uri; // Use the first repo if we cannot determine a repo from the uri. const gitRepo = (uri ? getRepositoryForFile(gitAPI, uri) : gitAPI.repositories[0]) ?? gitAPI.repositories[0]; @@ -155,10 +150,9 @@ export function getLink(gitAPI: GitAPI, useSelection: boolean, hostPrefix?: stri } const blobSegment = (gitRepo.state.HEAD?.ahead === 0) ? `/blob/${linkType === 'headlink' ? gitRepo.state.HEAD.name : gitRepo.state.HEAD?.commit}` : ''; - const fileSegments = fileAndPosition && uri ? (fileAndPosition.type === LinkType.File + const fileSegments = fileAndPosition.type === LinkType.File ? (useSelection ? `${uri.path.substring(gitRepo.rootUri.path.length)}${useRange ? rangeString(fileAndPosition.range) : ''}` : '') - : (useSelection ? `${uri.path.substring(gitRepo.rootUri.path.length)}${useRange ? notebookCellRangeString(fileAndPosition.cellIndex, fileAndPosition.range) : ''}` : '')) - : ''; + : (useSelection ? `${uri.path.substring(gitRepo.rootUri.path.length)}${useRange ? notebookCellRangeString(fileAndPosition.cellIndex, fileAndPosition.range) : ''}` : ''); return `${hostPrefix}/${repo.owner}/${repo.repo}${blobSegment }${fileSegments}`; diff --git a/src/vs/editor/contrib/clipboard/browser/clipboard.ts b/src/vs/editor/contrib/clipboard/browser/clipboard.ts index e9a71900cf12c..420abfa16a49d 100644 --- a/src/vs/editor/contrib/clipboard/browser/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/browser/clipboard.ts @@ -16,6 +16,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import * as nls from 'vs/nls'; import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -107,6 +108,9 @@ export const CopyAction = supportsCopy ? registerCommand(new MultiCommand({ MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { submenu: MenuId.MenubarCopy, title: { value: nls.localize('copy as', "Copy As"), original: 'Copy As', }, group: '2_ccp', order: 3 }); MenuRegistry.appendMenuItem(MenuId.EditorContext, { submenu: MenuId.EditorContextCopy, title: { value: nls.localize('copy as', "Copy As"), original: 'Copy As', }, group: CLIPBOARD_CONTEXT_MENU_GROUP, order: 3 }); +MenuRegistry.appendMenuItem(MenuId.EditorContext, { submenu: MenuId.EditorContextShare, title: { value: nls.localize('share', "Share"), original: 'Share', }, group: '11_share', order: -1, when: ContextKeyExpr.and(ContextKeyExpr.notEquals('resourceScheme', 'output'), EditorContextKeys.editorTextFocus) }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { submenu: MenuId.EditorTitleContextShare, title: { value: nls.localize('share', "Share"), original: 'Share', }, group: '11_share', order: -1 }); +MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { submenu: MenuId.ExplorerContextShare, title: { value: nls.localize('share', "Share"), original: 'Share', }, group: '11_share', order: -1 }); export const PasteAction = supportsPaste ? registerCommand(new MultiCommand({ id: 'editor.action.clipboardPasteAction', diff --git a/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts index e0a59867d9b18..4eef122381b1f 100644 --- a/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts @@ -161,18 +161,7 @@ export class ContextMenuController implements IEditorContribution { // get menu groups const menu = this._menuService.createMenu(menuId, this._contextKeyService); - const groups = menu.getActions({ - arg: [model.uri, this._editor.getSelections()?.map((selection) => ({ - start: { - line: selection.getStartPosition().lineNumber - 1, - character: selection.getStartPosition().column - }, - end: { - line: selection.getEndPosition().lineNumber - 1, - character: selection.getEndPosition().column - } - }))] - }); + const groups = menu.getActions({ arg: model.uri }); menu.dispose(); // translate them into other actions diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 6faa88dee3dda..34551c1a5d5ee 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -98,18 +98,15 @@ export class MenuId { static readonly MenubarViewMenu = new MenuId('MenubarViewMenu'); static readonly MenubarHomeMenu = new MenuId('MenubarHomeMenu'); static readonly OpenEditorsContext = new MenuId('OpenEditorsContext'); - static readonly OpenEditorsContextShare = new MenuId('OpenEditorsContextShare'); static readonly ProblemsPanelContext = new MenuId('ProblemsPanelContext'); static readonly SCMChangeContext = new MenuId('SCMChangeContext'); static readonly SCMResourceContext = new MenuId('SCMResourceContext'); - static readonly SCMResourceContextShare = new MenuId('SCMResourceContextShare'); static readonly SCMResourceFolderContext = new MenuId('SCMResourceFolderContext'); static readonly SCMResourceGroupContext = new MenuId('SCMResourceGroupContext'); static readonly SCMSourceControl = new MenuId('SCMSourceControl'); static readonly SCMTitle = new MenuId('SCMTitle'); static readonly SearchContext = new MenuId('SearchContext'); static readonly SearchActionMenu = new MenuId('SearchActionContext'); - static readonly Share = new MenuId('Share'); static readonly StatusBarWindowIndicatorMenu = new MenuId('StatusBarWindowIndicatorMenu'); static readonly StatusBarRemoteIndicatorMenu = new MenuId('StatusBarRemoteIndicatorMenu'); static readonly StickyScrollContext = new MenuId('StickyScrollContext'); @@ -480,9 +477,7 @@ export class MenuItemAction implements IAction { run(...args: any[]): Promise { let runArgs: any[] = []; - if (this._options?.arg && Array.isArray(this._options.arg)) { - runArgs = [...runArgs, ...this._options.arg]; - } else if (this._options?.arg) { + if (this._options?.arg) { runArgs = [...runArgs, this._options.arg]; } diff --git a/src/vs/workbench/contrib/codeEditor/browser/editorLineNumberMenu.ts b/src/vs/workbench/contrib/codeEditor/browser/editorLineNumberMenu.ts index e287bba453a13..4baeaca8412e0 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/editorLineNumberMenu.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/editorLineNumberMenu.ts @@ -88,6 +88,9 @@ export class EditorLineNumberContextMenu extends Disposable implements IEditorCo actions.push(collectedActions); } + const menuActions = menu.getActions({ arg: { lineNumber, uri: model.uri }, shouldForwardArgs: true }); + actions.push(...menuActions.map(a => a[1])); + // if the current editor selections do not contain the target line number, // set the selection to the clicked line number if (e.target.type === MouseTargetType.GUTTER_LINE_NUMBERS) { @@ -104,24 +107,11 @@ export class EditorLineNumberContextMenu extends Disposable implements IEditorCo } } - const ranges = this.editor.getSelections()?.map((selection) => ({ - start: { - line: selection.getStartPosition().lineNumber - 1, - character: selection.getStartPosition().column - }, - end: { - line: selection.getEndPosition().lineNumber - 1, - character: selection.getEndPosition().column - } - })); - const menuActions = menu.getActions({ arg: { lineNumber, uri: model.uri, ranges } }); - actions.push(...menuActions.map(a => a[1])); - this.contextMenuService.showContextMenu({ getAnchor: () => anchor, getActions: () => Separator.join(...actions), menuActionOptions: { shouldForwardArgs: true }, - getActionsContext: () => ({ lineNumber, uri: model.uri, ranges }), + getActionsContext: () => ({ lineNumber, uri: model.uri }), onHide: () => menu.dispose(), }); }); diff --git a/src/vs/workbench/contrib/share/browser/share.contribution.ts b/src/vs/workbench/contrib/share/browser/share.contribution.ts deleted file mode 100644 index 38a06e736e4d4..0000000000000 --- a/src/vs/workbench/contrib/share/browser/share.contribution.ts +++ /dev/null @@ -1,64 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { DisposableStore } from 'vs/base/common/lifecycle'; -import { IMenuService, MenuId, MenuRegistry, SubmenuItemAction } from 'vs/platform/actions/common/actions'; -import { ContextKeyExpr, ContextKeyExpression, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import * as nls from 'vs/nls'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; - -const menuIds: [MenuId, MenuId | undefined, ContextKeyExpression | undefined][] = [ - [MenuId.EditorContext, MenuId.EditorContextShare, ContextKeyExpr.and(ContextKeyExpr.notEquals('resourceScheme', 'output'), EditorContextKeys.editorTextFocus)], - [MenuId.EditorTitleContext, MenuId.EditorTitleContextShare, undefined], - [MenuId.ExplorerContext, MenuId.ExplorerContextShare, undefined], - [MenuId.OpenEditorsContext, MenuId.OpenEditorsContextShare, undefined], - [MenuId.SCMResourceContext, MenuId.SCMResourceContextShare, undefined], - [MenuId.EditorLineNumberContext, undefined, undefined], -]; - -for (const [menuId, submenuId, when] of menuIds) { - if (submenuId !== undefined) { - MenuRegistry.appendMenuItem(menuId, { submenu: submenuId, title: { value: nls.localize('share', "Share"), original: 'Share', }, group: '11_share', order: -1, when }); - } -} - -class ShareContribution { - - private readonly disposableStore = new DisposableStore(); - private readonly menu = this.menuService.createMenu(MenuId.Share, this.contextKeyService); - - constructor( - @IMenuService private readonly menuService: IMenuService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - ) { - this.ensureActions(); - this.menu.onDidChange(() => this.ensureActions()); - } - - private ensureActions(): void { - this.disposableStore.clear(); - - const allActions = this.menu.getActions(); - - for (const [_, actions] of allActions) { - for (const action of actions) { - if (action instanceof SubmenuItemAction || !action.enabled) { - continue; - } - for (const [menuId, submenuId] of menuIds) { - this.disposableStore.add(MenuRegistry.appendMenuItem( - submenuId ?? menuId, - { command: action.item } - )); - } - } - } - } -} - -Registry.as(Extensions.Workbench).registerWorkbenchContribution(ShareContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index e95eba66981ec..f19d36af35763 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -129,11 +129,6 @@ const apiMenus: IAPIMenu[] = [ id: MenuId.SCMResourceContext, description: localize('menus.resourceStateContext', "The Source Control resource state context menu") }, - { - key: 'scm/resourceState/context/share', - id: MenuId.SCMResourceContextShare, - description: localize('menus.resourceStateContextShare', "The Share submenu in the Source Control resource state context menu") - }, { key: 'scm/resourceFolder/context', id: MenuId.SCMResourceFolderContext, @@ -300,13 +295,6 @@ const apiMenus: IAPIMenu[] = [ description: localize('menus.share', "Share submenu shown in the top level File menu."), proposed: 'contribShareMenu' }, - { - key: 'share', - id: MenuId.Share, - description: localize('share', "Share submenus in all relevant context menus."), - supportsSubmenus: false, - proposed: 'contribShareMenu' - }, { key: 'editor/inlineCompletions/actions', id: MenuId.InlineCompletionsActions, diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 52d79cfa34b5f..d1945cb9b2334 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -358,6 +358,4 @@ import 'vs/workbench/contrib/deprecatedExtensionMigrator/browser/deprecatedExten // Bracket Pair Colorizer 2 Telemetry import 'vs/workbench/contrib/bracketPairColorizer2Telemetry/browser/bracketPairColorizer2Telemetry.contribution'; -import 'vs/workbench/contrib/share/browser/share.contribution'; - //#endregion