diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index ff35e0a5a6f..9fa7fc9f28f 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -87,6 +87,7 @@ const vscodeResources = [ 'out-build/vs/workbench/services/files/**/*.md', 'out-build/vs/code/electron-browser/sharedProcess/sharedProcess.js', 'out-build/vs/code/electron-browser/issue/issueReporter.js', + 'out-build/vs/code/electron-browser/processExplorer/processExplorer.js', '!**/test/**' ]; diff --git a/src/vs/code/buildfile.js b/src/vs/code/buildfile.js index 0293acbb5be..2ff372605b7 100644 --- a/src/vs/code/buildfile.js +++ b/src/vs/code/buildfile.js @@ -12,7 +12,7 @@ function createModuleDescription(name, exclude) { excludes = excludes.concat(exclude); } result.exclude= excludes; - + return result; } @@ -21,7 +21,8 @@ exports.collectModules= function() { createModuleDescription('vs/code/electron-main/main', []), createModuleDescription('vs/code/node/cli', []), createModuleDescription('vs/code/node/cliProcessMain', ['vs/code/node/cli']), + createModuleDescription('vs/code/electron-browser/issue/issueReporterMain', []), createModuleDescription('vs/code/electron-browser/sharedProcess/sharedProcessMain', []), - createModuleDescription('vs/code/electron-browser/issue/issueReporterMain', []) + createModuleDescription('vs/code/electron-browser/processExplorer/processExplorerMain', []) ]; -}; \ No newline at end of file +}; diff --git a/src/vs/code/electron-browser/processExplorer/media/processExplorer.css b/src/vs/code/electron-browser/processExplorer/media/processExplorer.css new file mode 100644 index 00000000000..6f38b441592 --- /dev/null +++ b/src/vs/code/electron-browser/processExplorer/media/processExplorer.css @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +html, +body { + margin: 0; + padding: 0; + height: 100%; + width: 100%; + -webkit-touch-callout: none; + -webkit-user-select: none; + user-select: none; + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; + font-size: 13px; + color: #cccccc; +} + +.cpu { + width: 45px; +} + +.pid { + width: 50px +} + +.memory { + width: 90px; +} + +.process-item { + line-height: 22px; +} + +table { + border-collapse: collapse; + width: 100%; + table-layout: fixed; +} +th { + vertical-align: bottom; + border-bottom: 1px solid #cccccc; + padding: .5rem; + border-top: 1px solid #cccccc; + text-align: center; +} +td { + padding: .25rem; + vertical-align: top; +} + +.centered { + text-align: center; +} + +.data { + white-space: pre; +} + +tbody > tr:hover { + background-color: #2A2D2E; +} diff --git a/src/vs/code/electron-browser/processExplorer/processExplorer.html b/src/vs/code/electron-browser/processExplorer/processExplorer.html new file mode 100644 index 00000000000..4d6930d9987 --- /dev/null +++ b/src/vs/code/electron-browser/processExplorer/processExplorer.html @@ -0,0 +1,17 @@ + + + + + + + + + + +
+ + + + + + \ No newline at end of file diff --git a/src/vs/code/electron-browser/processExplorer/processExplorer.js b/src/vs/code/electron-browser/processExplorer/processExplorer.js new file mode 100644 index 00000000000..1fdb4bb8296 --- /dev/null +++ b/src/vs/code/electron-browser/processExplorer/processExplorer.js @@ -0,0 +1,175 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const path = require('path'); +const fs = require('fs'); +const remote = require('electron').remote; + +function assign(destination, source) { + return Object.keys(source) + .reduce(function (r, key) { r[key] = source[key]; return r; }, destination); +} + +function parseURLQueryArgs() { + const search = window.location.search || ''; + + return search.split(/[?&]/) + .filter(function (param) { return !!param; }) + .map(function (param) { return param.split('='); }) + .filter(function (param) { return param.length === 2; }) + .reduce(function (r, param) { r[param[0]] = decodeURIComponent(param[1]); return r; }, {}); +} + +function uriFromPath(_path) { + var pathName = path.resolve(_path).replace(/\\/g, '/'); + if (pathName.length > 0 && pathName.charAt(0) !== '/') { + pathName = '/' + pathName; + } + + return encodeURI('file://' + pathName); +} + +function readFile(file) { + return new Promise(function(resolve, reject) { + fs.readFile(file, 'utf8', function(err, data) { + if (err) { + reject(err); + return; + } + resolve(data); + }); + }); +} + +function main() { + const args = parseURLQueryArgs(); + const configuration = JSON.parse(args['config'] || '{}') || {}; + + assign(process.env, configuration.userEnv); + + //#region Add support for using node_modules.asar + (function () { + const path = require('path'); + const Module = require('module'); + let NODE_MODULES_PATH = path.join(configuration.appRoot, 'node_modules'); + if (/[a-z]\:/.test(NODE_MODULES_PATH)) { + // Make drive letter uppercase + NODE_MODULES_PATH = NODE_MODULES_PATH.charAt(0).toUpperCase() + NODE_MODULES_PATH.substr(1); + } + const NODE_MODULES_ASAR_PATH = NODE_MODULES_PATH + '.asar'; + + const originalResolveLookupPaths = Module._resolveLookupPaths; + Module._resolveLookupPaths = function (request, parent, newReturn) { + const result = originalResolveLookupPaths(request, parent, newReturn); + + const paths = newReturn ? result : result[1]; + for (let i = 0, len = paths.length; i < len; i++) { + if (paths[i] === NODE_MODULES_PATH) { + paths.splice(i, 0, NODE_MODULES_ASAR_PATH); + break; + } + } + + return result; + }; + })(); + //#endregion + + // Get the nls configuration into the process.env as early as possible. + var nlsConfig = { availableLanguages: {} }; + const config = process.env['VSCODE_NLS_CONFIG']; + if (config) { + process.env['VSCODE_NLS_CONFIG'] = config; + try { + nlsConfig = JSON.parse(config); + } catch (e) { /*noop*/ } + } + + if (nlsConfig._resolvedLanguagePackCoreLocation) { + let bundles = Object.create(null); + nlsConfig.loadBundle = function(bundle, language, cb) { + let result = bundles[bundle]; + if (result) { + cb(undefined, result); + return; + } + let bundleFile = path.join(nlsConfig._resolvedLanguagePackCoreLocation, bundle.replace(/\//g, '!') + '.nls.json'); + readFile(bundleFile).then(function (content) { + let json = JSON.parse(content); + bundles[bundle] = json; + cb(undefined, json); + }) + .catch(cb); + }; + } + + var locale = nlsConfig.availableLanguages['*'] || 'en'; + if (locale === 'zh-tw') { + locale = 'zh-Hant'; + } else if (locale === 'zh-cn') { + locale = 'zh-Hans'; + } + + window.document.documentElement.setAttribute('lang', locale); + + const extractKey = function (e) { + return [ + e.ctrlKey ? 'ctrl-' : '', + e.metaKey ? 'meta-' : '', + e.altKey ? 'alt-' : '', + e.shiftKey ? 'shift-' : '', + e.keyCode + ].join(''); + }; + + const TOGGLE_DEV_TOOLS_KB = (process.platform === 'darwin' ? 'meta-alt-73' : 'ctrl-shift-73'); // mac: Cmd-Alt-I, rest: Ctrl-Shift-I + const RELOAD_KB = (process.platform === 'darwin' ? 'meta-82' : 'ctrl-82'); // mac: Cmd-R, rest: Ctrl-R + + window.addEventListener('keydown', function (e) { + const key = extractKey(e); + if (key === TOGGLE_DEV_TOOLS_KB) { + remote.getCurrentWebContents().toggleDevTools(); + } else if (key === RELOAD_KB) { + remote.getCurrentWindow().reload(); + } + }); + + // Load the loader + const loaderFilename = configuration.appRoot + '/out/vs/loader.js'; + const loaderSource = fs.readFileSync(loaderFilename); + require('vm').runInThisContext(loaderSource, { filename: loaderFilename }); + var define = global.define; + global.define = undefined; + + window.nodeRequire = require.__$__nodeRequire; + + define('fs', ['original-fs'], function (originalFS) { return originalFS; }); // replace the patched electron fs with the original node fs for all AMD code + + window.MonacoEnvironment = {}; + const rootUrl = uriFromPath(configuration.appRoot) + '/out'; + + require.config({ + baseUrl: rootUrl, + 'vs/nls': nlsConfig, + nodeCachedDataDir: configuration.nodeCachedDataDir, + nodeModules: [/*BUILD->INSERT_NODE_MODULES*/] + }); + + if (nlsConfig.pseudo) { + require(['vs/nls'], function (nlsPlugin) { + nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); + }); + } + + require([ + 'vs/code/electron-browser/processExplorer/processExplorerMain' + ], function (processExplorer) { + processExplorer.startup(configuration.data); + }); +} + +main(); diff --git a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts new file mode 100644 index 00000000000..1fb3ca60b3b --- /dev/null +++ b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts @@ -0,0 +1,184 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'vs/css!./media/processExplorer'; +import { listProcesses, ProcessItem } from 'vs/base/node/ps'; +import { remote, webFrame } from 'electron'; +import { repeat } from 'vs/base/common/strings'; +import { totalmem } from 'os'; +import product from 'vs/platform/node/product'; +import { localize } from 'vs/nls'; +import { ProcessExplorerData, ProcessExplorerStyles } from '../../../platform/issue/common/issue'; +import * as browser from 'vs/base/browser/browser'; +import * as platform from 'vs/base/common/platform'; + +let processList: any[]; + +function getProcessList(rootProcess: ProcessItem) { + const processes: any[] = []; + + if (rootProcess) { + getProcessItem(processes, rootProcess, 0); + } + + return processes; +} + +function getProcessItem(processes: any[], item: ProcessItem, indent: number): void { + const isRoot = (indent === 0); + + const MB = 1024 * 1024; + + // Format name with indent + const name = isRoot ? `${product.applicationName} main` : item.name; + const formattedName = isRoot ? name : `${repeat(' ', indent)} ${name}`; + const memory = process.platform === 'win32' ? item.mem : (totalmem() * (item.mem / 100)); + processes.push({ + cpu: Number(item.load.toFixed(0)), + memory: Number((memory / MB).toFixed(0)), + pid: Number((item.pid).toFixed(0)), + name, + formattedName, + cmd: item.cmd + }); + + // Recurse into children if any + if (Array.isArray(item.children)) { + item.children.forEach(child => getProcessItem(processes, child, indent + 1)); + } +} + +function getProcessIdWithHighestProperty(processList, propertyName: string) { + let max = 0; + let maxProcessId; + processList.forEach(process => { + if (process[propertyName] > max) { + max = process[propertyName]; + maxProcessId = process.pid; + } + }); + + return maxProcessId; +} + +function updateProcessInfo(processList): void { + const target = document.getElementById('process-list'); + const highestCPUProcess = getProcessIdWithHighestProperty(processList, 'cpu'); + const highestMemoryProcess = getProcessIdWithHighestProperty(processList, 'memory'); + + let tableHtml = ` + + ${localize('cpu', "CPU %")} + ${localize('memory', "Memory (MB)")} + ${localize('pid', "pid")} + ${localize('name', "Name")} + `; + + processList.forEach(p => { + const cpuClass = p.pid === highestCPUProcess ? 'highest' : ''; + const memoryClass = p.pid === highestMemoryProcess ? 'highest' : ''; + + tableHtml += ` + + ${p.cpu} + ${p.memory} + ${p.pid} + ${p.formattedName} + `; + }); + + target.innerHTML = `${tableHtml}
`; +} + +function applyStyles(styles: ProcessExplorerStyles): void { + const styleTag = document.createElement('style'); + const content: string[] = []; + + if (styles.hoverBackground) { + content.push(`tbody > tr:hover { background-color: ${styles.hoverBackground}; }`); + } + + if (styles.hoverForeground) { + content.push(`tbody > tr:hover{ color: ${styles.hoverForeground}; }`); + } + + if (styles.highlightForeground) { + content.push(`.highest { color: ${styles.highlightForeground}; }`); + } + + styleTag.innerHTML = content.join('\n'); + document.head.appendChild(styleTag); + document.body.style.color = styles.color; +} + +function applyZoom(zoomLevel: number): void { + webFrame.setZoomLevel(zoomLevel); + browser.setZoomFactor(webFrame.getZoomFactor()); + // See https://github.com/Microsoft/vscode/issues/26151 + // Cannot be trusted because the webFrame might take some time + // until it really applies the new zoom level + browser.setZoomLevel(webFrame.getZoomLevel(), /*isTrusted*/false); +} + +function showContextMenu(e) { + e.preventDefault(); + + const pid = parseInt(e.currentTarget.id); + if (pid && typeof pid === 'number') { + const menu = new remote.Menu(); + menu.append(new remote.MenuItem({ + label: localize('killProcess', "Kill Process"), + click() { + process.kill(pid, 'SIGTERM'); + } + }) + ); + + menu.append(new remote.MenuItem({ + label: localize('forceKillProcess', "Force Kill Process"), + click() { + process.kill(pid, 'SIGKILL'); + } + }) + ); + + menu.popup(remote.getCurrentWindow()); + } +} + +export function startup(data: ProcessExplorerData): void { + applyStyles(data.styles); + applyZoom(data.zoomLevel); + + setInterval(() => listProcesses(remote.process.pid).then(processes => { + processList = getProcessList(processes); + updateProcessInfo(processList); + + const tableRows = document.getElementsByTagName('tr'); + for (let i = 0; i < tableRows.length; i++) { + const tableRow = tableRows[i]; + tableRow.addEventListener('click', (e) => { + showContextMenu(e); + }); + } + }), 1200); + + + document.onkeydown = (e: KeyboardEvent) => { + const cmdOrCtrlKey = platform.isMacintosh ? e.metaKey : e.ctrlKey; + + // Cmd/Ctrl + zooms in + if (cmdOrCtrlKey && e.keyCode === 187) { + applyZoom(webFrame.getZoomLevel() + 1); + } + + // Cmd/Ctrl - zooms out + if (cmdOrCtrlKey && e.keyCode === 189) { + applyZoom(webFrame.getZoomLevel() - 1); + } + }; +} \ No newline at end of file diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 46d965ac212..dad88c4fc00 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -942,6 +942,8 @@ export class CodeMenu { } }, false)); + const openProcessExplorer = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miOpenProcessExplorerer', comment: ['&& denotes a mnemonic'] }, "Open &&Process Explorer")), click: () => this.runActionInRenderer('workbench.action.openProcessExplorer') }); + let reportIssuesItem: Electron.MenuItem = null; if (product.reportIssueUrl) { const label = nls.localize({ key: 'miReportIssue', comment: ['&& denotes a mnemonic', 'Translate this to "Report Issue in English" in all languages please!'] }, "Report &&Issue"); @@ -990,7 +992,8 @@ export class CodeMenu { }) : null, (product.licenseUrl || product.privacyStatementUrl) ? __separator__() : null, toggleDevToolsItem, - isWindows && product.quality !== 'stable' ? showAccessibilityOptions : null + openProcessExplorer, + isWindows && product.quality !== 'stable' ? showAccessibilityOptions : null, ]).forEach(item => helpMenu.append(item)); if (!isMacintosh) { @@ -1029,7 +1032,7 @@ export class CodeMenu { } private openAccessibilityOptions(): void { - let win = new BrowserWindow({ + const win = new BrowserWindow({ alwaysOnTop: true, skipTaskbar: true, resizable: false, diff --git a/src/vs/platform/issue/common/issue.ts b/src/vs/platform/issue/common/issue.ts index 8955b50b076..228bcdfefb1 100644 --- a/src/vs/platform/issue/common/issue.ts +++ b/src/vs/platform/issue/common/issue.ts @@ -11,6 +11,15 @@ import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensio export const IIssueService = createDecorator('issueService'); +export interface WindowStyles { + backgroundColor: string; + color: string; +} +export interface WindowData { + styles: WindowStyles; + zoomLevel: number; +} + export enum IssueType { Bug, PerformanceIssue, @@ -18,9 +27,7 @@ export enum IssueType { SettingsSearchIssue } -export interface IssueReporterStyles { - backgroundColor: string; - color: string; +export interface IssueReporterStyles extends WindowStyles { textLinkColor: string; inputBackground: string; inputForeground: string; @@ -35,9 +42,8 @@ export interface IssueReporterStyles { sliderActiveColor: string; } -export interface IssueReporterData { +export interface IssueReporterData extends WindowData { styles: IssueReporterStyles; - zoomLevel: number; enabledExtensions: ILocalExtension[]; issueType?: IssueType; } @@ -58,7 +64,18 @@ export interface ISettingsSearchIssueReporterData extends IssueReporterData { export interface IssueReporterFeatures { } +export interface ProcessExplorerStyles extends WindowStyles { + hoverBackground: string; + hoverForeground: string; + highlightForeground: string; +} + +export interface ProcessExplorerData extends WindowData { + styles: ProcessExplorerStyles; +} + export interface IIssueService { _serviceBrand: any; openReporter(data: IssueReporterData): TPromise; + openProcessExplorer(data: ProcessExplorerData): TPromise; } diff --git a/src/vs/platform/issue/common/issueIpc.ts b/src/vs/platform/issue/common/issueIpc.ts index 99d73534b5e..d804c6579fe 100644 --- a/src/vs/platform/issue/common/issueIpc.ts +++ b/src/vs/platform/issue/common/issueIpc.ts @@ -7,7 +7,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IIssueService, IssueReporterData } from './issue'; +import { IIssueService, IssueReporterData, ProcessExplorerData } from './issue'; export interface IIssueChannel extends IChannel { call(command: 'openIssueReporter', arg: IssueReporterData): TPromise; @@ -23,6 +23,8 @@ export class IssueChannel implements IIssueChannel { switch (command) { case 'openIssueReporter': return this.service.openReporter(arg); + case 'openProcessExplorer': + return this.service.openProcessExplorer(arg); } return undefined; } @@ -37,4 +39,8 @@ export class IssueChannelClient implements IIssueService { openReporter(data: IssueReporterData): TPromise { return this.channel.call('openIssueReporter', data); } + + openProcessExplorer(data: ProcessExplorerData): TPromise { + return this.channel.call('openProcessExplorer', data); + } } \ No newline at end of file diff --git a/src/vs/platform/issue/electron-main/issueService.ts b/src/vs/platform/issue/electron-main/issueService.ts index 7232efc90fb..1d589bf8f16 100644 --- a/src/vs/platform/issue/electron-main/issueService.ts +++ b/src/vs/platform/issue/electron-main/issueService.ts @@ -9,7 +9,7 @@ import { TPromise, Promise } from 'vs/base/common/winjs.base'; import { localize } from 'vs/nls'; import * as objects from 'vs/base/common/objects'; import { parseArgs } from 'vs/platform/environment/node/argv'; -import { IIssueService, IssueReporterData, IssueReporterFeatures } from 'vs/platform/issue/common/issue'; +import { IIssueService, IssueReporterData, IssueReporterFeatures, ProcessExplorerData } from 'vs/platform/issue/common/issue'; import { BrowserWindow, ipcMain, screen } from 'electron'; import { ILaunchService } from 'vs/code/electron-main/launch'; import { getPerformanceInfo, PerformanceInfo, getSystemInfo, SystemInfo } from 'vs/code/electron-main/diagnostics'; @@ -22,7 +22,8 @@ const DEFAULT_BACKGROUND_COLOR = '#1E1E1E'; export class IssueService implements IIssueService { _serviceBrand: any; _issueWindow: BrowserWindow; - _parentWindow: BrowserWindow; + _issueParentWindow: BrowserWindow; + _processExplorerWindow: BrowserWindow; constructor( private machineId: string, @@ -46,11 +47,11 @@ export class IssueService implements IIssueService { }); ipcMain.on('workbenchCommand', (event, arg) => { - this._parentWindow.webContents.send('vscode:runAction', { id: arg, from: 'issueReporter' }); + this._issueParentWindow.webContents.send('vscode:runAction', { id: arg, from: 'issueReporter' }); }); - this._parentWindow = BrowserWindow.getFocusedWindow(); - const position = this.getWindowPosition(); + this._issueParentWindow = BrowserWindow.getFocusedWindow(); + const position = this.getWindowPosition(this._issueParentWindow, 800, 900); this._issueWindow = new BrowserWindow({ width: position.width, height: position.height, @@ -73,7 +74,54 @@ export class IssueService implements IIssueService { return TPromise.as(null); } - private getWindowPosition() { + openProcessExplorer(data: ProcessExplorerData): TPromise { + // Create as singleton + if (!this._processExplorerWindow) { + const position = this.getWindowPosition(BrowserWindow.getFocusedWindow(), 800, 300); + this._processExplorerWindow = new BrowserWindow({ + skipTaskbar: true, + resizable: true, + width: position.width, + height: position.height, + minWidth: 300, + minHeight: 200, + x: position.x, + y: position.y, + backgroundColor: data.styles.backgroundColor, + title: localize('processExplorer', "Process Explorer") + }); + + this._processExplorerWindow.setMenuBarVisibility(false); + + const windowConfiguration = { + appRoot: this.environmentService.appRoot, + nodeCachedDataDir: this.environmentService.nodeCachedDataDir, + windowId: this._processExplorerWindow.id, + userEnv: this.userEnv, + machineId: this.machineId, + data + }; + + const environment = parseArgs(process.argv); + const config = objects.assign(environment, windowConfiguration); + for (let key in config) { + if (config[key] === void 0 || config[key] === null || config[key] === '') { + delete config[key]; // only send over properties that have a true value + } + } + + this._processExplorerWindow.loadURL(`${require.toUrl('vs/code/electron-browser/processExplorer/processExplorer.html')}?config=${encodeURIComponent(JSON.stringify(config))}`); + + this._processExplorerWindow.on('close', () => this._processExplorerWindow = void 0); + } + + // Focus + this._processExplorerWindow.focus(); + + return TPromise.as(null); + } + + private getWindowPosition(parentWindow: BrowserWindow, defaultWidth: number, defaultHeight: number) { // We want the new window to open on the same display that the parent is in let displayToUse: Electron.Display; const displays = screen.getAllDisplays(); @@ -93,8 +141,8 @@ export class IssueService implements IIssueService { } // if we have a last active window, use that display for the new window - if (!displayToUse && this._parentWindow) { - displayToUse = screen.getDisplayMatching(this._parentWindow.getBounds()); + if (!displayToUse && parentWindow) { + displayToUse = screen.getDisplayMatching(parentWindow.getBounds()); } // fallback to primary display or first display @@ -104,8 +152,8 @@ export class IssueService implements IIssueService { } let state = { - width: 800, - height: 900, + width: defaultWidth, + height: defaultHeight, x: undefined, y: undefined }; diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 3e0b42414db..7abd5ddb6b1 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -871,6 +871,24 @@ export class OpenIssueReporterAction extends Action { } } +export class OpenProcessExplorer extends Action { + public static readonly ID = 'workbench.action.openProcessExplorer'; + public static readonly LABEL = nls.localize('openProcessExplorer', "Open Process Explorer"); + + constructor( + id: string, + label: string, + @IWorkbenchIssueService private issueService: IWorkbenchIssueService + ) { + super(id, label); + } + + public run(): TPromise { + return this.issueService.openProcessExplorer() + .then(() => true); + } +} + export class ReportPerformanceIssueUsingReporterAction extends Action { public static readonly ID = 'workbench.action.reportPerformanceIssueUsingReporter'; public static readonly LABEL = nls.localize('reportPerformanceIssue', "Report Performance Issue"); diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index c57c28d3c1b..26d69e90739 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -14,7 +14,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, ToggleMenuBarAction, CloseWorkspaceAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, ShowStartupPerformance, ToggleSharedProcessAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, ShowAboutDialogAction, InspectContextKeysAction } from 'vs/workbench/electron-browser/actions'; +import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, ToggleMenuBarAction, CloseWorkspaceAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, ShowStartupPerformance, ToggleSharedProcessAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, ShowAboutDialogAction, InspectContextKeysAction, OpenProcessExplorer } from 'vs/workbench/electron-browser/actions'; import { registerCommands } from 'vs/workbench/electron-browser/commands'; import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, OpenFolderAsWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -111,6 +111,7 @@ const developerCategory = nls.localize('developer', "Developer"); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowStartupPerformance, ShowStartupPerformance.ID, ShowStartupPerformance.LABEL), 'Developer: Startup Performance', developerCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSharedProcessAction, ToggleSharedProcessAction.ID, ToggleSharedProcessAction.LABEL), 'Developer: Toggle Shared Process', developerCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(InspectContextKeysAction, InspectContextKeysAction.ID, InspectContextKeysAction.LABEL), 'Developer: Inspect Context Keys', developerCategory); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenProcessExplorer, OpenProcessExplorer.ID, OpenProcessExplorer.LABEL), 'Developer: Open Process Explorer', developerCategory); const recentFilesPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(inRecentFilesPickerContextKey)); diff --git a/src/vs/workbench/services/issue/common/issue.ts b/src/vs/workbench/services/issue/common/issue.ts index b998241ce1c..e97d68ea091 100644 --- a/src/vs/workbench/services/issue/common/issue.ts +++ b/src/vs/workbench/services/issue/common/issue.ts @@ -14,4 +14,5 @@ export const IWorkbenchIssueService = createDecorator('w export interface IWorkbenchIssueService { _serviceBrand: any; openReporter(dataOverrides?: Partial): TPromise; + openProcessExplorer(): TPromise; } diff --git a/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts b/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts index 7e78f2904a5..80d5a39cad2 100644 --- a/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts +++ b/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts @@ -8,7 +8,7 @@ import { IssueReporterStyles, IIssueService, IssueReporterData } from 'vs/platform/issue/common/issue'; import { TPromise } from 'vs/base/common/winjs.base'; import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { textLinkForeground, inputBackground, inputBorder, inputForeground, buttonBackground, buttonHoverBackground, buttonForeground, inputValidationErrorBorder, foreground, inputActiveOptionBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry'; +import { textLinkForeground, inputBackground, inputBorder, inputForeground, buttonBackground, buttonHoverBackground, buttonForeground, inputValidationErrorBorder, foreground, inputActiveOptionBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, editorBackground, editorForeground, listHoverBackground, listHoverForeground, listHighlightForeground } from 'vs/platform/theme/common/colorRegistry'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IExtensionManagementService, IExtensionEnablementService, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; import { webFrame } from 'electron'; @@ -41,6 +41,21 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { return this.issueService.openReporter(issueReporterData); }); } + + openProcessExplorer(): TPromise { + const theme = this.themeService.getTheme(); + const data = { + zoomLevel: webFrame.getZoomLevel(), + styles: { + backgroundColor: theme.getColor(editorBackground) && theme.getColor(editorBackground).toString(), + color: theme.getColor(editorForeground).toString(), + hoverBackground: theme.getColor(listHoverBackground) && theme.getColor(listHoverBackground).toString(), + hoverForeground: theme.getColor(listHoverForeground) && theme.getColor(listHoverForeground).toString(), + highlightForeground: theme.getColor(listHighlightForeground) && theme.getColor(listHighlightForeground).toString() + } + }; + return this.issueService.openProcessExplorer(data); + } } export function getIssueReporterStyles(theme: ITheme): IssueReporterStyles { diff --git a/tslint.json b/tslint.json index 31396c2aa56..9a59614556e 100644 --- a/tslint.json +++ b/tslint.json @@ -409,6 +409,18 @@ "*" // node modules ] }, + { + "target": "**/vs/code/electron-browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "vs/nls", + "**/vs/base/**", + "**/vs/platform/**", + "**/vs/code/**", + "*" // node modules + ] + }, { "target": "**/vs/code/**", "restrictions": [