Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Adds custom terminal launch settings #3495

Merged
merged 8 commits into from
Apr 18, 2016
Merged
Show file tree
Hide file tree
Changes from 5 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 @@ -19,6 +19,40 @@ import {ITerminalService} from 'vs/workbench/parts/execution/common/execution';
import {SyncActionDescriptor} from 'vs/platform/actions/common/actions';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
import {Extensions, IConfigurationRegistry} from 'vs/platform/configuration/common/configurationRegistry';
import {DEFAILT_WINDOWS_TERM, DEFAULT_LINUX_TERM} from 'vs/workbench/parts/execution/electron-browser/terminal';

let configurationRegistry = <IConfigurationRegistry>Registry.as(Extensions.Configuration);
configurationRegistry.registerConfiguration({
'id': 'terminal',
'order': 100,
'title': nls.localize('terminalConfigurationTitle', "Terminal configuration"),
'type': 'object',
'properties': {
'terminal.windows': {
'description': nls.localize('terminal.windows', "Windows specific settings."),
'type': 'object',
'properties': {
'exec': {
'type': 'string',
'description': nls.localize('terminal.windows.exec', "Customizes which terminal to run."),
'default': DEFAILT_WINDOWS_TERM
}
}
},
'terminal.linux': {
'description': nls.localize('terminal.linux', "Linux specific settings."),
'type': 'object',
'properties': {
'exec': {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you thought about how the settings should be named given that the eventual integrated terminal #143 will likely have a set of settings as well? Maybe something like terminal.external... and terminal.integrated... or something?

Copy link
Author

@pflannery pflannery Apr 18, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Tyriar This wasn't related to #143. This just solved the hard coded terminal issue for windows and Linux.
Imo I would of thought #143 would be better as extension.

I can change the name if you like.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just forward thinking, I'm currently experimenting with the integrated terminal so I want to make sure the settings have good names to prevent changes/inconsistencies in the future. I'm thinking lets rename them to this for now:

terminal.external.windowsExec
terminal.external.linuxExec

I'll see what @joaomoreno thinks of the naming later

'type': 'string',
'description': nls.localize('terminal.linux.exec', "Customizes which terminal to run."),
'default': DEFAULT_LINUX_TERM
}
}
}
}
});

export class OpenConsoleAction extends Action {

Expand Down
18 changes: 18 additions & 0 deletions src/vs/workbench/parts/execution/electron-browser/terminal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import fs = require('fs');
import env = require('vs/base/common/platform');

export let DEFAULT_LINUX_TERM = 'x-terminal-emulator';

// if we're not on debian and using gnome then
// set default to gnome-terminal
if (env.isLinux
&& fs.existsSync('/etc/debian_version') === false
&& process.env.DESKTOP_SESSION === 'gnome') {
DEFAULT_LINUX_TERM = 'gnome-terminal';
}

export const DEFAILT_WINDOWS_TERM = 'cmd';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo DEFAULT

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import uri from 'vs/base/common/uri';
import {TPromise} from 'vs/base/common/winjs.base';
import {ITerminalService} from 'vs/workbench/parts/execution/common/execution';
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
import {IMessageService} from 'vs/platform/message/common/message';
import {DEFAILT_WINDOWS_TERM, DEFAULT_LINUX_TERM} from 'vs/workbench/parts/execution/electron-browser/terminal';

import cp = require('child_process');
import processes = require('vs/base/node/processes');
Expand All @@ -18,13 +18,36 @@ export class WinTerminalService implements ITerminalService {
public serviceId = ITerminalService;

constructor(
@IConfigurationService private _configurationService: IConfigurationService,
@IMessageService private _messageService: IMessageService
@IConfigurationService private _configurationService: IConfigurationService
) {
}

public openTerminal(path: string): void {
cp.spawn(processes.getWindowsShell(), ['/c', 'start', '/wait'], { cwd: path });
this._configurationService.loadConfiguration().done(configuration => {
return new Promise((success, failed) => {
this.spawnTerminal(
cp,
configuration,
processes.getWindowsShell(),
path,
success,
err => {
errors.onUnexpectedError(err);
failed(err);
}
);
});
}, errors.onUnexpectedError);
}

private spawnTerminal(spawner, configuration, command: string, path: string, onExit, onError) {
let terminalConfig = configuration.terminal;
let exec = terminalConfig.windows.exec || DEFAILT_WINDOWS_TERM;
let cmdArgs = ['/c', 'start', '/wait', exec];

let child = spawner.spawn(command, cmdArgs, { cwd: path });
child.on('error', onError);
child.on('exit', onExit);
}
}

Expand All @@ -49,14 +72,41 @@ export class MacTerminalService implements ITerminalService {
child.on('exit', (code: number) => {
c(code === 0 ? 'iterm.scpt' : 'terminal.scpt');
});
}).then(name => uri.parse(require.toUrl(`vs/workbench/parts/execution/electron-browser/${ name }`)).fsPath);
}).then(name => uri.parse(require.toUrl(`vs/workbench/parts/execution/electron-browser/${name}`)).fsPath);
}
}

export class LinuxTerminalService implements ITerminalService {
public serviceId = ITerminalService;

constructor(
@IConfigurationService private _configurationService: IConfigurationService
) { }


public openTerminal(path: string): void {
cp.spawn('x-terminal-emulator', [], { cwd: path });
this._configurationService.loadConfiguration().done(configuration => {
return new Promise((success, failed) => {
this.spawnTerminal(
cp,
configuration,
path,
success,
err => {
errors.onUnexpectedError(err);
failed(err);
}
);
});
}, errors.onUnexpectedError);
}

private spawnTerminal(spawner, configuration, path: string, onExit, onError) {
let terminalConfig = configuration.terminal;
let exec = terminalConfig.linux.exec || DEFAULT_LINUX_TERM;
const child = spawner.spawn(exec, [], { cwd: path });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does just setting the cwd work with gnome-terminal? You may need to pass in --working-directory=path for that?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node runs gnome-terminal at cwd specified in the options. I tested on Fedora 23 and it does go to the cwd specified. I think it's just a useful flag when someone wants to run from a different path other than the cwd gnome-terminal was executed from.

child.on('error', onError);
child.on('exit', onExit);
}

}
129 changes: 129 additions & 0 deletions src/vs/workbench/parts/execution/test/browser/terminalService.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*---------------------------------------------------------------------------------------------
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also live in electron-browser/.

* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

'use strict';

import {equal} from 'assert';
import {WinTerminalService, LinuxTerminalService} from 'vs/workbench/parts/execution/electron-browser/terminalService';
import {DEFAILT_WINDOWS_TERM, DEFAULT_LINUX_TERM} from 'vs/workbench/parts/execution/electron-browser/terminal';

suite('Execution - TerminalService', () => {
let mockOnExit;
let mockOnError;
let mockConfig;

setup(() => {
mockConfig = {
terminal: {
windows: {
exec: 'testWindowsShell'
},
linux: {
exec: 'testLinuxShell'
}
}
};
mockOnExit = s => s;
mockOnError = e => e;
});

test("WinTerminalService - uses terminal from configuration", done => {
let testShell = 'cmd';
let testCwd = 'path/to/workspace';
let mockSpawner = {
spawn: (command, args, opts) => {
// assert
equal(command, testShell, 'shell should equal expected');
equal(args[args.length - 1], mockConfig.terminal.windows.exec, 'terminal should equal expected')
equal(opts.cwd, testCwd, 'opts.cwd should equal expected');
done();
return {
on: (evt) => evt
}
}
};
let testService = new WinTerminalService(mockConfig);
(<any>testService).spawnTerminal(
mockSpawner,
mockConfig,
testShell,
testCwd,
mockOnExit,
mockOnError
);
});

test("WinTerminalService - uses default terminal when configuration.terminal.windows.exec is undefined", done => {
let testShell = 'cmd';
let testCwd = 'path/to/workspace';
let mockSpawner = {
spawn: (command, args, opts) => {
// assert
equal(args[args.length - 1], DEFAILT_WINDOWS_TERM, 'terminal should equal expected')
done();
return {
on: (evt) => evt
}
}
};
mockConfig.terminal.windows.exec = undefined;
let testService = new WinTerminalService(mockConfig);
(<any>testService).spawnTerminal(
mockSpawner,
mockConfig,
testShell,
testCwd,
mockOnExit,
mockOnError
);
});

test("LinuxTerminalService - uses terminal from configuration", done => {
let testCwd = 'path/to/workspace';
let mockSpawner = {
spawn: (command, args, opts) => {
// assert
equal(command, mockConfig.terminal.linux.exec, 'terminal should equal expected');
equal(opts.cwd, testCwd, 'opts.cwd should equal expected');
done();
return {
on: (evt) => evt
}
}
};
let testService = new LinuxTerminalService(mockConfig);
(<any>testService).spawnTerminal(
mockSpawner,
mockConfig,
testCwd,
mockOnExit,
mockOnError
);
});

test("LinuxTerminalService - uses default terminal when configuration.terminal.linux.exec is undefined", done => {
let testCwd = 'path/to/workspace';
let mockSpawner = {
spawn: (command, args, opts) => {
// assert
equal(command, DEFAULT_LINUX_TERM, 'terminal should equal expected')
done();
return {
on: (evt) => evt
}
}
};
mockConfig.terminal.linux.exec = undefined;
let testService = new LinuxTerminalService(mockConfig);
(<any>testService).spawnTerminal(
mockSpawner,
mockConfig,
testCwd,
mockOnExit,
mockOnError
);
});

});