Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Change Open in Terminal context menu title dynamically based on settings #99543

Merged
merged 3 commits into from
Jun 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,11 @@

import * as nls from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import * as paths from 'vs/base/common/path';
import { URI } from 'vs/base/common/uri';
import { IExternalTerminalConfiguration, IExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal';
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED } from 'vs/workbench/contrib/terminal/common/terminal';
import { MenuId, MenuRegistry, IMenuItem } from 'vs/platform/actions/common/actions';
import { ITerminalService as IIntegratedTerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { ResourceContextKey } from 'vs/workbench/common/resources';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IFileService } from 'vs/platform/files/common/files';
import { IListService } from 'vs/platform/list/browser/listService';
import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files';
Expand All @@ -25,9 +20,13 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { IExplorerService } from 'vs/workbench/contrib/files/common/files';
import { isWeb } from 'vs/base/common/platform';
import { IPathService } from 'vs/workbench/services/path/common/pathService';

import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { Disposable } from 'vs/base/common/lifecycle';
import { isWeb, isWindows } from 'vs/base/common/platform';
import { dirname, basename } from 'vs/base/common/path';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';

const OPEN_IN_TERMINAL_COMMAND_ID = 'openInTerminal';
CommandsRegistry.registerCommand({
Expand Down Expand Up @@ -62,89 +61,78 @@ CommandsRegistry.registerCommand({
authority: resource.authority,
fragment: resource.fragment,
query: resource.query,
path: paths.dirname(resource.path)
path: dirname(resource.path)
});
}).forEach(cwd => {
if (opened[cwd.path]) {
return;
}
opened[cwd.path] = true;
const instance = integratedTerminalService.createTerminal({ cwd });
if (instance && (resources.length === 1 || !resource || cwd.path === resource.path || cwd.path === paths.dirname(resource.path))) {
if (instance && (resources.length === 1 || !resource || cwd.path === resource.path || cwd.path === dirname(resource.path))) {
integratedTerminalService.setActiveInstance(instance);
integratedTerminalService.showPanel(true);
}
});
} else {
distinct(targets.map(({ stat }) => stat!.isDirectory ? stat!.resource.fsPath : paths.dirname(stat!.resource.fsPath))).forEach(cwd => {
distinct(targets.map(({ stat }) => stat!.isDirectory ? stat!.resource.fsPath : dirname(stat!.resource.fsPath))).forEach(cwd => {
terminalService!.openTerminal(cwd);
});
}
});
}
});

if (!isWeb) {
const OPEN_NATIVE_CONSOLE_COMMAND_ID = 'workbench.action.terminal.openNativeConsole';
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: OPEN_NATIVE_CONSOLE_COMMAND_ID,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C,
when: KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED,
weight: KeybindingWeight.WorkbenchContrib,
handler: async (accessor) => {
const historyService = accessor.get(IHistoryService);
// Open external terminal in local workspaces
const terminalService = accessor.get(IExternalTerminalService);
const root = historyService.getLastActiveWorkspaceRoot(Schemas.file);
if (root) {
terminalService.openTerminal(root.fsPath);
} else {
// Opens current file's folder, if no folder is open in editor
const activeFile = historyService.getLastActiveFile(Schemas.file);
if (activeFile) {
terminalService.openTerminal(paths.dirname(activeFile.fsPath));
} else {
const pathService = accessor.get(IPathService);
const userHome = await pathService.userHome;
terminalService.openTerminal(userHome.fsPath);
}
export class ExternalTerminalContribution extends Disposable implements IWorkbenchContribution {
private _openInTerminalMenuItem: IMenuItem;

constructor(
@IConfigurationService private readonly _configurationService: IConfigurationService
) {
super();

this._openInTerminalMenuItem = {
group: 'navigation',
order: 30,
command: {
id: OPEN_IN_TERMINAL_COMMAND_ID,
title: nls.localize('scopedConsoleAction', "Open in Terminal")
},
when: ContextKeyExpr.or(ResourceContextKey.Scheme.isEqualTo(Schemas.file), ResourceContextKey.Scheme.isEqualTo(Schemas.vscodeRemote))
};
MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, this._openInTerminalMenuItem);
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, this._openInTerminalMenuItem);

this._configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('terminal.explorerKind') || e.affectsConfiguration('terminal.external')) {
this._refreshOpenInTerminalMenuItemTitle();
}
});
this._refreshOpenInTerminalMenuItemTitle();
}

private _refreshOpenInTerminalMenuItemTitle(): void {
if (isWeb) {
this._openInTerminalMenuItem.command.title = nls.localize('scopedConsoleAction.integrated', "Open in Integrated Terminal");
return;
}

const config = this._configurationService.getValue<IExternalTerminalConfiguration>().terminal;
if (config.explorerKind === 'integrated') {
this._openInTerminalMenuItem.command.title = nls.localize('scopedConsoleAction.integrated', "Open in Integrated Terminal");
return;
}
});

MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: OPEN_NATIVE_CONSOLE_COMMAND_ID,
title: { value: nls.localize('globalConsoleAction', "Open New External Terminal"), original: 'Open New External Terminal' }
if (isWindows && config.external.windowsExec) {
const file = basename(config.external.windowsExec);
if (file === 'wt' || file === 'wt.exe') {
this._openInTerminalMenuItem.command.title = nls.localize('scopedConsoleAction.wt', "Open in Windows Terminal");
return;
}
}
});

this._openInTerminalMenuItem.command.title = nls.localize('scopedConsoleAction.external', "Open in External Terminal");
}
}

const openConsoleCommand = {
id: OPEN_IN_TERMINAL_COMMAND_ID,
title: nls.localize('scopedConsoleAction', "Open in Terminal")
};
MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, {
group: 'navigation',
order: 30,
command: openConsoleCommand,
when: ResourceContextKey.Scheme.isEqualTo(Schemas.file)
});
MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, {
group: 'navigation',
order: 30,
command: openConsoleCommand,
when: ResourceContextKey.Scheme.isEqualTo(Schemas.vscodeRemote)
});
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
group: 'navigation',
order: 30,
command: openConsoleCommand,
when: ResourceContextKey.Scheme.isEqualTo(Schemas.file)
});
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
group: 'navigation',
order: 30,
command: openConsoleCommand,
when: ResourceContextKey.Scheme.isEqualTo(Schemas.vscodeRemote)
});
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExternalTerminalContribution, LifecyclePhase.Restored);
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
import * as paths from 'vs/base/common/path';
import { IExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal';
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED } from 'vs/workbench/contrib/terminal/common/terminal';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { Schemas } from 'vs/base/common/network';
import { IPathService } from 'vs/workbench/services/path/common/pathService';
import { WindowsExternalTerminalService, MacExternalTerminalService, LinuxExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/node/externalTerminalService';
import { IConfigurationRegistry, Extensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { isWindows, isMacintosh, isLinux } from 'vs/base/common/platform';
import { DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/node/externalTerminal';

const OPEN_NATIVE_CONSOLE_COMMAND_ID = 'workbench.action.terminal.openNativeConsole';
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: OPEN_NATIVE_CONSOLE_COMMAND_ID,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C,
when: KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED,
weight: KeybindingWeight.WorkbenchContrib,
handler: async (accessor) => {
const historyService = accessor.get(IHistoryService);
// Open external terminal in local workspaces
const terminalService = accessor.get(IExternalTerminalService);
const root = historyService.getLastActiveWorkspaceRoot(Schemas.file);
if (root) {
terminalService.openTerminal(root.fsPath);
} else {
// Opens current file's folder, if no folder is open in editor
const activeFile = historyService.getLastActiveFile(Schemas.file);
if (activeFile) {
terminalService.openTerminal(paths.dirname(activeFile.fsPath));
} else {
const pathService = accessor.get(IPathService);
const userHome = await pathService.userHome;
terminalService.openTerminal(userHome.fsPath);
}
}
}
});

MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: OPEN_NATIVE_CONSOLE_COMMAND_ID,
title: { value: nls.localize('globalConsoleAction', "Open New External Terminal"), original: 'Open New External Terminal' }
}
});

if (isWindows) {
registerSingleton(IExternalTerminalService, WindowsExternalTerminalService, true);
} else if (isMacintosh) {
registerSingleton(IExternalTerminalService, MacExternalTerminalService, true);
} else if (isLinux) {
registerSingleton(IExternalTerminalService, LinuxExternalTerminalService, true);
}

LinuxExternalTerminalService.getDefaultTerminalLinuxReady().then(defaultTerminalLinux => {
let configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
configurationRegistry.registerConfiguration({
id: 'externalTerminal',
order: 100,
title: nls.localize('terminalConfigurationTitle', "External Terminal"),
type: 'object',
properties: {
'terminal.explorerKind': {
type: 'string',
enum: [
'integrated',
'external'
],
enumDescriptions: [
nls.localize('terminal.explorerKind.integrated', "Use VS Code's integrated terminal."),
nls.localize('terminal.explorerKind.external', "Use the configured external terminal.")
],
description: nls.localize('explorer.openInTerminalKind', "Customizes what kind of terminal to launch."),
default: 'integrated'
},
'terminal.external.windowsExec': {
type: 'string',
description: nls.localize('terminal.external.windowsExec', "Customizes which terminal to run on Windows."),
default: WindowsExternalTerminalService.getDefaultTerminalWindows(),
scope: ConfigurationScope.APPLICATION
},
'terminal.external.osxExec': {
type: 'string',
description: nls.localize('terminal.external.osxExec', "Customizes which terminal application to run on macOS."),
default: DEFAULT_TERMINAL_OSX,
scope: ConfigurationScope.APPLICATION
},
'terminal.external.linuxExec': {
type: 'string',
description: nls.localize('terminal.external.linuxExec', "Customizes which terminal to run on Linux."),
default: defaultTerminalLinux,
scope: ConfigurationScope.APPLICATION
}
}
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

export const DEFAULT_TERMINAL_OSX = 'Terminal.app';
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
*--------------------------------------------------------------------------------------------*/

import { deepEqual, equal } from 'assert';
import { WindowsExternalTerminalService, LinuxExternalTerminalService, MacExternalTerminalService, DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/node/externalTerminalService';
import { WindowsExternalTerminalService, LinuxExternalTerminalService, MacExternalTerminalService } from 'vs/workbench/contrib/externalTerminal/node/externalTerminalService';
import { DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/node/externalTerminal';

suite('ExternalTerminalService', () => {
let mockOnExit: Function;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,10 @@ import { assign } from 'vs/base/common/objects';
import { IExternalTerminalService, IExternalTerminalConfiguration, IExternalTerminalSettings } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { IConfigurationRegistry, Extensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { optional } from 'vs/platform/instantiation/common/instantiation';

import { DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/node/externalTerminal';

const TERMINAL_TITLE = nls.localize('console.title', "VS Code Console");
export const DEFAULT_TERMINAL_OSX = 'Terminal.app';

export class WindowsExternalTerminalService implements IExternalTerminalService {
public _serviceBrand: undefined;
Expand Down Expand Up @@ -358,54 +354,3 @@ function quote(args: string[]): string {
}
return r;
}

if (env.isWindows) {
registerSingleton(IExternalTerminalService, WindowsExternalTerminalService, true);
} else if (env.isMacintosh) {
registerSingleton(IExternalTerminalService, MacExternalTerminalService, true);
} else if (env.isLinux) {
registerSingleton(IExternalTerminalService, LinuxExternalTerminalService, true);
}

LinuxExternalTerminalService.getDefaultTerminalLinuxReady().then(defaultTerminalLinux => {
let configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
configurationRegistry.registerConfiguration({
id: 'externalTerminal',
order: 100,
title: nls.localize('terminalConfigurationTitle', "External Terminal"),
type: 'object',
properties: {
'terminal.explorerKind': {
type: 'string',
enum: [
'integrated',
'external'
],
enumDescriptions: [
nls.localize('terminal.explorerKind.integrated', "Use VS Code's integrated terminal."),
nls.localize('terminal.explorerKind.external', "Use the configured external terminal.")
],
description: nls.localize('explorer.openInTerminalKind', "Customizes what kind of terminal to launch."),
default: 'integrated'
},
'terminal.external.windowsExec': {
type: 'string',
description: nls.localize('terminal.external.windowsExec', "Customizes which terminal to run on Windows."),
default: WindowsExternalTerminalService.getDefaultTerminalWindows(),
scope: ConfigurationScope.APPLICATION
},
'terminal.external.osxExec': {
type: 'string',
description: nls.localize('terminal.external.osxExec', "Customizes which terminal application to run on macOS."),
default: DEFAULT_TERMINAL_OSX,
scope: ConfigurationScope.APPLICATION
},
'terminal.external.linuxExec': {
type: 'string',
description: nls.localize('terminal.external.linuxExec', "Customizes which terminal to run on Linux."),
default: defaultTerminalLinux,
scope: ConfigurationScope.APPLICATION
}
}
});
});
4 changes: 2 additions & 2 deletions src/vs/workbench/workbench.desktop.main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ import 'vs/workbench/contrib/remote/electron-browser/remote.contribution';
// CodeEditor Contributions
import 'vs/workbench/contrib/codeEditor/electron-browser/codeEditor.contribution';

// Execution
import 'vs/workbench/contrib/externalTerminal/node/externalTerminalService';
// External Terminal
import 'vs/workbench/contrib/externalTerminal/node/externalTerminal.contribution';

// Performance
import 'vs/workbench/contrib/performance/electron-browser/performance.contribution';
Expand Down