diff --git a/package.json b/package.json index 4a72af69404dd0d8a1271719b2dda3cc1a509d30..4077013383924f4d7faa0f609a254ba7511abb50 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,10 @@ "https-proxy-agent": "^0.3.5", "iconv-lite": "^0.4.13", "native-keymap": "^0.1.2", + "pty.js": "^0.3.0", "sax": "^1.1.1", "semver": "^4.2.0", + "term.js": "0.0.7", "vscode-debugprotocol": "1.8.0-pre.3", "vscode-textmate": "^1.0.11", "winreg": "0.0.12", diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts new file mode 100644 index 0000000000000000000000000000000000000000..9fbb0644ecd7bcbd5e5a29e8096453d5d3393359 --- /dev/null +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * 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 {TPromise} from 'vs/base/common/winjs.base'; +import {createDecorator, ServiceIdentifier} from 'vs/platform/instantiation/common/instantiation'; + +export const TERMINAL_PANEL_ID = 'workbench.panel.terminal'; + +export const TERMINAL_SERVICE_ID = 'terminalService'; + +export var ITerminalService = createDecorator(TERMINAL_SERVICE_ID); + +export interface ITerminalService { + serviceId: ServiceIdentifier; + + show(): TPromise; +} diff --git a/src/vs/workbench/parts/terminal/node/media/terminal.contribution.css b/src/vs/workbench/parts/terminal/node/media/terminal.contribution.css new file mode 100644 index 0000000000000000000000000000000000000000..ed76aefc0631d0eb688ab1d7f0c712a3f4cfc8ec --- /dev/null +++ b/src/vs/workbench/parts/terminal/node/media/terminal.contribution.css @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .integrated-terminal { + align-items: flex-end; + display: flex; +} + +.monaco-workbench .integrated-terminal .terminal { + background-color: #1e1e1e!important; +} + +.monaco-workbench .terminal-cursor { + background-color: #1e1e1e; +} + +.monaco-workbench .reverse-video { + color: #CCC; +} + +.vs-dark .monaco-workbench .terminal-cursor { + background-color: #CCC; +} + +.vs-dark .monaco-workbench .reverse-video { + color: #252526; +} + +/* High Contrast Theming */ + +.hc-dark .monaco-workbench .integrated-terminal .terminal { + background-color: black!important; +} + +.hc-dark .monaco-workbench .terminal-cursor { + background-color: white; +} + +.hc-dark .monaco-workbench .reverse-video { + color: black; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/node/pty.js.d.ts b/src/vs/workbench/parts/terminal/node/pty.js.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..0c44c428c51a09b022f0be5ffe31e823a622664b --- /dev/null +++ b/src/vs/workbench/parts/terminal/node/pty.js.d.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'pty.js' { + export function fork(file: string, args: string[], options: any): Terminal; + export function spawn(file: string, args: string[], options: any): Terminal; + export function createTerminal(file: string, args: string[], options: any): Terminal; + + export interface Terminal { + on(event: string, callback: (data: any) => void): void; + resize(columns: number, rows: number): void; + write(data: string): void; + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/node/term.js.d.ts b/src/vs/workbench/parts/terminal/node/term.js.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..9f2c4c7ea9ed2c20c7259d90295269d7b15eb83d --- /dev/null +++ b/src/vs/workbench/parts/terminal/node/term.js.d.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'term.js' { + function init(): TermJsTerminal; + + // There seems to be no way to export this so it can be referenced outside of this file when a + // module is a function. + interface TermJsTerminal { + on(event: string, callback: (data: any) => void): void; + resize(columns: number, rows: number): void; + } + + export = init; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/node/terminal.contribution.ts b/src/vs/workbench/parts/terminal/node/terminal.contribution.ts new file mode 100644 index 0000000000000000000000000000000000000000..3360a59d19a46b18b7a72d4524a197637e4a6cca --- /dev/null +++ b/src/vs/workbench/parts/terminal/node/terminal.contribution.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/terminal.contribution'; +import nls = require('vs/nls'); +//import {KeyMod, KeyCode} from 'vs/base/common/keyCodes'; +import {SyncActionDescriptor} from 'vs/platform/actions/common/actions'; +import {registerSingleton} from 'vs/platform/instantiation/common/extensions'; +import {IWorkbenchActionRegistry, Extensions as ActionExtensions} from 'vs/workbench/common/actionRegistry'; +import {TerminalService} from 'vs/workbench/parts/terminal/node/terminalService'; +import {ToggleTerminalAction} from 'vs/workbench/parts/terminal/node/terminalActions'; +import {ITerminalService, TERMINAL_PANEL_ID} from 'vs/workbench/parts/terminal/common/terminal'; +import * as panel from 'vs/workbench/browser/panel'; +import {Registry} from 'vs/platform/platform'; +import {Extensions, IConfigurationRegistry} from 'vs/platform/configuration/common/configurationRegistry'; + +let configurationRegistry = Registry.as(Extensions.Configuration); +configurationRegistry.registerConfiguration({ + 'id': 'terminal', + 'order': 100, + 'title': nls.localize('integratedTerminalConfigurationTitle', "Integrated terminal configuration"), + 'type': 'object', + 'properties': { + 'terminal.integrated.enabled': { + 'description': nls.localize('terminal.integrated.enabled', "(Experimental) Enable the integrated terminal."), + 'type': 'boolean', + 'default': false + } + } +}); + +// Register Service +registerSingleton(ITerminalService, TerminalService); + +// Register Output Panel +(Registry.as(panel.Extensions.Panels)).registerPanel(new panel.PanelDescriptor( + 'vs/workbench/parts/terminal/node/terminalPanel', + 'TerminalPanel', + TERMINAL_PANEL_ID, + nls.localize('terminal', "Terminal"), + 'terminal' +)); + +// register toggle output action globally +let actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); +/*actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTerminalAction, ToggleTerminalAction.ID, ToggleTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.US_BACKTICK +}), nls.localize('viewCategory', "View"));*/ +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTerminalAction, ToggleTerminalAction.ID, ToggleTerminalAction.LABEL), nls.localize('viewCategory', "View")); diff --git a/src/vs/workbench/parts/terminal/node/terminalActions.ts b/src/vs/workbench/parts/terminal/node/terminalActions.ts new file mode 100644 index 0000000000000000000000000000000000000000..a260a2d5a515b6937f4522c48e06821a73492dfe --- /dev/null +++ b/src/vs/workbench/parts/terminal/node/terminalActions.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import {TPromise} from 'vs/base/common/winjs.base'; +import nls = require('vs/nls'); +import {Action} from 'vs/base/common/actions'; +import {IPartService} from 'vs/workbench/services/part/common/partService'; +import {IPanelService} from 'vs/workbench/services/panel/common/panelService'; +import {TERMINAL_PANEL_ID, ITerminalService} from 'vs/workbench/parts/terminal/common/terminal'; + +export class ToggleTerminalAction extends Action { + + public static ID = 'workbench.action.terminal.toggleTerminal'; + public static LABEL = nls.localize('toggleTerminal', "Toggle Terminal"); + + constructor( + id: string, label: string, + @IPartService private partService: IPartService, + @IPanelService private panelService: IPanelService, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + const panel = this.panelService.getActivePanel(); + if (panel && panel.getId() === TERMINAL_PANEL_ID) { + this.partService.setPanelHidden(true); + + return TPromise.as(null); + } + + return this.terminalService.show(); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/node/terminalPanel.ts b/src/vs/workbench/parts/terminal/node/terminalPanel.ts new file mode 100644 index 0000000000000000000000000000000000000000..cebb5b15bf5a435bb346c177428f34cec27fca49 --- /dev/null +++ b/src/vs/workbench/parts/terminal/node/terminalPanel.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import termJs = require('term.js'); +import fs = require('fs'); +import {fork, Terminal} from 'pty.js'; +import {TPromise} from 'vs/base/common/winjs.base'; +import {Builder, Dimension} from 'vs/base/browser/builder'; +import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; +import {TERMINAL_PANEL_ID} from 'vs/workbench/parts/terminal/common/terminal'; +import {Panel} from 'vs/workbench/browser/panel'; +import {ScrollableElement} from 'vs/base/browser/ui/scrollbar/scrollableElementImpl'; +import {DomNodeScrollable} from 'vs/base/browser/ui/scrollbar/domNodeScrollable'; + +const TERMINAL_CHAR_WIDTH = 8; +const TERMINAL_CHAR_HEIGHT = 18; + +export class TerminalPanel extends Panel { + + private ptyProcess: Terminal; + private parentDomElement: HTMLElement; + private terminal; + private terminalDomElement: HTMLDivElement; + + constructor( + @ITelemetryService telemetryService: ITelemetryService + ) { + super(TERMINAL_PANEL_ID, telemetryService); + } + + public layout(dimension: Dimension): void { + let cols = Math.floor(this.parentDomElement.offsetWidth / TERMINAL_CHAR_WIDTH); + let rows = Math.floor(this.parentDomElement.offsetHeight / TERMINAL_CHAR_HEIGHT); + this.terminal.resize(cols, rows); + this.ptyProcess.resize(cols, rows); + } + + public create(parent: Builder): TPromise { + super.create(parent); + + this.ptyProcess = fork(process.env.SHELL || 'sh', [], { + name: fs.existsSync('/usr/share/terminfo/x/xterm-256color') ? 'xterm-256color' : 'xterm', + cols: 80, + rows: 6, + cwd: process.env.HOME + }); + this.parentDomElement = parent.getHTMLElement(); + this.terminalDomElement = document.createElement('div'); + this.parentDomElement.classList.add('integrated-terminal'); + let terminalScrollable = new DomNodeScrollable(this.terminalDomElement); + let terminalContainer = new ScrollableElement(this.terminalDomElement, terminalScrollable, { horizontal: 'hidden', vertical: 'auto' }); + this.terminal = termJs(); + + this.ptyProcess.on('data', (data) => { + this.terminal.write(data); + }); + this.terminal.on('data', (data) => { + this.ptyProcess.write(data); + return false; + }); + + this.terminal.open(this.terminalDomElement); + this.parentDomElement.appendChild(terminalContainer.getDomNode()); + + this.terminalDomElement.style.fontFamily = 'Hack, mono'; + this.terminal.colors = [ + '#000000', // black + '#cd3131', // red + '#09885a', // green + '#e5e510', // yellow + '#0451a5', // blue + '#bc05bc', // magenta + '#0598bc', // cyan + '#e5e5e5', // white + '#111111', // bright black + '#dc6f6f', // bright red + '#53ac8c', // bright green + '#eded58', // bright yellow + '#4f85c0', // bright blue + '#d050d0', // bright magenta + '#50b7d0', // bright cyan + '#FFFFFF', // bright white + ]; + + return TPromise.as(null); + } +} diff --git a/src/vs/workbench/parts/terminal/node/terminalService.ts b/src/vs/workbench/parts/terminal/node/terminalService.ts new file mode 100644 index 0000000000000000000000000000000000000000..7d5c3837d96cbfb0896dd3ffdbc410ae384bcc20 --- /dev/null +++ b/src/vs/workbench/parts/terminal/node/terminalService.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import {TPromise} from 'vs/base/common/winjs.base'; +import {IPanelService} from 'vs/workbench/services/panel/common/panelService'; +import {ITerminalService, TERMINAL_PANEL_ID} from 'vs/workbench/parts/terminal/common/terminal'; + +export class TerminalService implements ITerminalService { + public serviceId = ITerminalService; + + constructor( + @IPanelService private panelService: IPanelService + ) { + } + + public show(): TPromise { + return this.panelService.openPanel(TERMINAL_PANEL_ID, true); + } +} \ No newline at end of file diff --git a/src/vs/workbench/workbench.main.js b/src/vs/workbench/workbench.main.js index ffa5618f20c6c445bc582b6427b8892ddd9710ec..77b25bd7d3159add04601795894ef4fe4c11d844 100644 --- a/src/vs/workbench/workbench.main.js +++ b/src/vs/workbench/workbench.main.js @@ -58,6 +58,8 @@ define([ 'vs/workbench/parts/output/browser/output.contribution', + 'vs/workbench/parts/terminal/node/terminal.contribution', + 'vs/workbench/parts/markdown/browser/markdown.contribution', 'vs/workbench/parts/markdown/browser/markdownActions.contribution',