Merge from master

This commit is contained in:
Raj Musuku
2019-02-21 17:56:04 -08:00
parent 5a146e34fa
commit 666ae11639
11482 changed files with 119352 additions and 255574 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +0,0 @@
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<!-- // {{SQL CARBON EDIT}}
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data:; media-src 'none'; child-src 'self'; object-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https:;">
-->
</head>
<body class="monaco-shell vs-dark" aria-label="">
<script src="preload.js"></script>
</body>
<!-- // {{SQL CARBON EDIT}} -->
<script src="../../../../sql/parts/grid/load/loadJquery.js"></script>
<script src="../../../../../node_modules/slickgrid/lib/jquery.event.drag-2.3.0.js"></script>
<script src="../../../../../node_modules/slickgrid/lib/jquery-ui-1.9.2.js"></script>
<!-- Startup via index.js -->
<script src="index.js"></script>
</html>

View File

@@ -1,369 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// Warning: Do not use the `let` declarator in this file, it breaks our minification
'use strict';
/*global window,document,define*/
const perf = require('../../../base/common/performance');
perf.mark('renderer/started');
const path = require('path');
const fs = require('fs');
const electron = require('electron');
const remote = electron.remote;
const ipc = electron.ipcRenderer;
process.lazyEnv = new Promise(function (resolve) {
const handle = setTimeout(function () {
resolve();
console.warn('renderer did not receive lazyEnv in time');
}, 10000);
ipc.once('vscode:acceptShellEnv', function (event, shellEnv) {
clearTimeout(handle);
assign(process.env, shellEnv);
resolve(process.env);
});
ipc.send('vscode:fetchShellEnv');
});
Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API)
function onError(error, enableDeveloperTools) {
if (enableDeveloperTools) {
remote.getCurrentWebContents().openDevTools();
}
console.error('[uncaught exception]: ' + error);
if (error.stack) {
console.error(error.stack);
}
}
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; }, {});
}
// {{SQL CARBON EDIT}}
function createScript(src, onload) {
const script = document.createElement('script');
script.src = src;
script.addEventListener('load', onload);
const head = document.getElementsByTagName('head')[0];
head.insertBefore(script, head.lastChild);
}
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 showPartsSplash(configuration) {
perf.mark('willShowPartsSplash');
// TODO@Ben remove me after a while
perf.mark('willAccessLocalStorage');
let storage = window.localStorage;
perf.mark('didAccessLocalStorage');
let data;
try {
let raw = storage.getItem('storage://global/parts-splash-data');
data = JSON.parse(raw);
} catch (e) {
// ignore
}
if (data) {
const splash = document.createElement('div');
splash.id = data.id;
const { layoutInfo, colorInfo } = data;
// ensure there is enough space
layoutInfo.sideBarWidth = Math.min(layoutInfo.sideBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth));
if (configuration.folderUri || configuration.workspace) {
// folder or workspace -> status bar color, sidebar
splash.innerHTML = `
<div style="position: absolute; width: 100%; left: 0; top: 0; height: ${layoutInfo.titleBarHeight}px; background-color: ${colorInfo.titleBarBackground};"></div>
<div style="position: absolute; height: calc(100% - ${layoutInfo.titleBarHeight}px); top: ${layoutInfo.titleBarHeight}px; ${layoutInfo.sideBarSide}: 0; width: ${layoutInfo.activityBarWidth}px; background-color: ${colorInfo.activityBarBackground};"></div>
<div style="position: absolute; height: calc(100% - ${layoutInfo.titleBarHeight}px); top: ${layoutInfo.titleBarHeight}px; ${layoutInfo.sideBarSide}: ${layoutInfo.activityBarWidth}px; width: ${layoutInfo.sideBarWidth}px; background-color: ${colorInfo.sideBarBackground};"></div>
<div style="position: absolute; width: 100%; bottom: 0; left: 0; height: ${layoutInfo.statusBarHeight}px; background-color: ${colorInfo.statusBarBackground};"></div>
`;
} else {
// empty -> speical status bar color, no sidebar
splash.innerHTML = `
<div style="position: absolute; width: 100%; left: 0; top: 0; height: ${layoutInfo.titleBarHeight}px; background-color: ${colorInfo.titleBarBackground};"></div>
<div style="position: absolute; height: calc(100% - ${layoutInfo.titleBarHeight}px); top: ${layoutInfo.titleBarHeight}px; ${layoutInfo.sideBarSide}: 0; width: ${layoutInfo.activityBarWidth}px; background-color: ${colorInfo.activityBarBackground};"></div>
<div style="position: absolute; width: 100%; bottom: 0; left: 0; height: ${layoutInfo.statusBarHeight}px; background-color: ${colorInfo.statusBarNoFolderBackground};"></div>
`;
}
document.body.appendChild(splash);
}
perf.mark('didShowPartsSplash');
}
const writeFile = (file, content) => new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c()));
function registerListeners(enableDeveloperTools) {
// Devtools & reload support
var listener;
if (enableDeveloperTools) {
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
listener = function (e) {
const key = extractKey(e);
if (key === TOGGLE_DEV_TOOLS_KB) {
remote.getCurrentWebContents().toggleDevTools();
} else if (key === RELOAD_KB) {
remote.getCurrentWindow().reload();
}
};
window.addEventListener('keydown', listener);
}
process.on('uncaughtException', function (error) { onError(error, enableDeveloperTools); });
return function () {
if (listener) {
window.removeEventListener('keydown', listener);
listener = void 0;
}
};
}
// {{SQL CARBON EDIT}}
/* eslint-disable */
// SQL global imports
require('slickgrid/slick.core');
const Slick = window.Slick;
require('slickgrid/slick.grid');
require('slickgrid/slick.editors');
require('slickgrid/slick.dataview');
require('reflect-metadata');
require('zone.js');
const _ = require('underscore')._;
function main() {
const webFrame = require('electron').webFrame;
const args = parseURLQueryArgs();
const configuration = JSON.parse(args['config'] || '{}') || {};
//#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
// Correctly inherit the parent's environment
assign(process.env, configuration.userEnv);
perf.importEntries(configuration.perfEntries);
showPartsSplash(configuration);
// 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((error) => {
try {
if (nlsConfig._corruptedFile) {
writeFile(nlsConfig._corruptedFile, 'corrupted').catch(function (error) { console.error(error); });
}
} finally {
cb(error, undefined);
}
});
};
}
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 enableDeveloperTools = (process.env['VSCODE_DEV'] || !!configuration.extensionDevelopmentPath) && !configuration.extensionTestsPath;
const unbind = registerListeners(enableDeveloperTools);
// disable pinch zoom & apply zoom level early to avoid glitches
const zoomLevel = configuration.zoomLevel;
webFrame.setVisualZoomLevelLimits(1, 1);
if (typeof zoomLevel === 'number' && zoomLevel !== 0) {
webFrame.setZoomLevel(zoomLevel);
}
// {{SQL CARBON EDIT}}
// Load the loader and start loading the workbench
const appRoot = uriFromPath(configuration.appRoot);
const rootUrl = appRoot + '/out';
createScript(appRoot + '/node_modules/chart.js/dist/Chart.js', undefined);
const loaderFilename = configuration.appRoot + '/out/vs/loader.js';
const loaderSource = require('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 onNodeCachedData = window.MonacoEnvironment.onNodeCachedData = [];
require.config({
baseUrl: uriFromPath(configuration.appRoot) + '/out',
'vs/nls': nlsConfig,
recordStats: !!configuration.performance,
nodeCachedDataDir: configuration.nodeCachedDataDir,
onNodeCachedData: function () { onNodeCachedData.push(arguments); },
nodeModules: [
'@angular/common',
'@angular/core',
'@angular/forms',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@angular/router',
'angular2-grid',
'ansi_up',
'pretty-data',
'html-query-plan',
'ng2-charts/ng2-charts',
'rxjs/Observable',
'rxjs/Subject',
'rxjs/Observer',
'htmlparser2',
'sanitize'
]
});
if (nlsConfig.pseudo) {
require(['vs/nls'], function (nlsPlugin) {
nlsPlugin.setPseudoTranslation(nlsConfig.pseudo);
});
}
// Perf Counters
window.MonacoEnvironment.timers = {
isInitialStartup: !!configuration.isInitialStartup,
hasAccessibilitySupport: !!configuration.accessibilitySupport,
start: configuration.perfStartTime,
windowLoad: configuration.perfWindowLoadTime
};
perf.mark('willLoadWorkbenchMain');
require([
'vs/workbench/workbench.main',
'vs/nls!vs/workbench/workbench.main',
'vs/css!vs/workbench/workbench.main'
], function () {
perf.mark('didLoadWorkbenchMain');
process.lazyEnv.then(function () {
perf.mark('main/startup');
require('vs/workbench/electron-browser/main')
.startup(configuration)
.done(function () {
unbind(); // since the workbench is running, unbind our developer related listeners and let the workbench handle them
}, function (error) {
onError(error, enableDeveloperTools);
});
});
});
}
main();

View File

@@ -1,39 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
(function() {
function getConfig() {
const queryParams = window.location.search.substring(1).split('&');
for (var i = 0; i < queryParams.length; i++) {
var kv = queryParams[i].split('=');
if (kv[0] === 'config' && kv[1]) {
return JSON.parse(decodeURIComponent(kv[1]));
}
}
return {};
}
try {
const config = getConfig();
const document = window.document;
// sets the base theme class ('vs', 'vs-dark', 'hc-black')
const baseTheme = config.baseTheme || 'vs';
document.body.className = 'monaco-shell ' + baseTheme;
// adds a stylesheet with the backgrdound color
var backgroundColor = config.backgroundColor;
if (!backgroundColor) {
backgroundColor = baseTheme === 'hc-black' ? '#000000' : (baseTheme === 'vs' ? '#FFFFFF' : '#1E1E1E');
}
const foregroundColor = baseTheme === 'hc-black' ? '#FFFFFF' : (baseTheme === 'vs' ? '#6C6C6C' : '#CCCCCC');
const style = document.createElement('style');
style.innerHTML = '.monaco-shell { background-color:' + backgroundColor + '; color:' + foregroundColor + '; }';
document.head.appendChild(style);
} catch (error) {
console.error(error);
}
})();

View File

@@ -3,15 +3,14 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { tmpdir } from 'os';
import { posix } from 'path';
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
import { List } from 'vs/base/browser/ui/list/listWidget';
import * as errors from 'vs/base/common/errors';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { WorkbenchListFocusContextKey, IListService, WorkbenchListSupportsMultiSelectContextKey, ListWidget, WorkbenchListHasSelectionOrFocus } from 'vs/platform/list/browser/listService';
import { PagedList } from 'vs/base/browser/ui/list/listPaging';
@@ -19,7 +18,12 @@ import { range } from 'vs/base/common/arrays';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { InEditorZenModeContext, NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor';
import { ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { URI } from 'vs/base/common/uri';
import { IDownloadService } from 'vs/platform/download/common/download';
import { generateUuid } from 'vs/base/common/uuid';
import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree';
import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree';
// --- List Commands
@@ -28,7 +32,7 @@ function ensureDOMFocus(widget: ListWidget): void {
// DOM focus is within another focusable control within the
// list/tree item. therefor we should ensure that the
// list/tree has DOM focus again after the command ran.
if (widget && !widget.isDOMFocused()) {
if (widget && widget.getHTMLElement() !== document.activeElement) {
widget.domFocus();
}
}
@@ -54,12 +58,25 @@ export function registerCommands(): void {
}
}
// ObjectTree
else if (focused instanceof ObjectTree || focused instanceof AsyncDataTree) {
const list = focused;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
list.focusNext(count, false, fakeKeyboardEvent);
const listFocus = list.getFocus();
if (listFocus.length) {
list.reveal(listFocus[0]);
}
}
// Tree
else if (focused) {
const tree = focused;
tree.focusNext(count, { origin: 'keyboard' });
tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError);
tree.reveal(tree.getFocus());
}
}
@@ -75,7 +92,7 @@ export function registerCommands(): void {
handler: (accessor, arg2) => focusDown(accessor, arg2)
});
function expandMultiSelection(focused: List<any> | PagedList<any> | ITree, previousFocus: any): void {
function expandMultiSelection(focused: List<any> | PagedList<any> | ITree | ObjectTree<any, any> | AsyncDataTree<any, any>, previousFocus: any): void {
// List
if (focused instanceof List || focused instanceof PagedList) {
@@ -90,6 +107,21 @@ export function registerCommands(): void {
}
}
// ObjectTree
else if (focused instanceof ObjectTree || focused instanceof AsyncDataTree) {
const list = focused;
const focus = list.getFocus() ? list.getFocus()[0] : void 0;
const selection = list.getSelection();
const fakeKeyboardEvent = new KeyboardEvent('keydown');
if (selection && selection.indexOf(focus) >= 0) {
list.setSelection(selection.filter(s => s !== previousFocus), fakeKeyboardEvent);
} else {
list.setSelection(selection.concat(focus), fakeKeyboardEvent);
}
}
// Tree
else if (focused) {
const tree = focused;
@@ -113,7 +145,7 @@ export function registerCommands(): void {
const focused = accessor.get(IListService).lastFocusedList;
// List
if (focused instanceof List || focused instanceof PagedList) {
if (focused instanceof List || focused instanceof PagedList || focused instanceof ObjectTree || focused instanceof AsyncDataTree) {
const list = focused;
// Focus down first
@@ -156,12 +188,25 @@ export function registerCommands(): void {
}
}
// ObjectTree
else if (focused instanceof ObjectTree || focused instanceof AsyncDataTree) {
const list = focused;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
list.focusPrevious(count, false, fakeKeyboardEvent);
const listFocus = list.getFocus();
if (listFocus.length) {
list.reveal(listFocus[0]);
}
}
// Tree
else if (focused) {
const tree = focused;
tree.focusPrevious(count, { origin: 'keyboard' });
tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError);
tree.reveal(tree.getFocus());
}
}
@@ -225,18 +270,39 @@ export function registerCommands(): void {
// Tree only
if (focused && !(focused instanceof List || focused instanceof PagedList)) {
const tree = focused;
const focus = tree.getFocus();
if (focused instanceof ObjectTree || focused instanceof AsyncDataTree) {
const tree = focused;
const focusedElements = tree.getFocus();
tree.collapse(focus).then(didCollapse => {
if (focus && !didCollapse) {
tree.focusParent({ origin: 'keyboard' });
return tree.reveal(tree.getFocus());
if (focusedElements.length === 0) {
return;
}
return void 0;
}).done(null, errors.onUnexpectedError);
const focus = focusedElements[0];
if (!tree.collapse(focus)) {
const parent = tree.getParentElement(focus);
if (parent) {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
tree.setFocus([parent], fakeKeyboardEvent);
tree.reveal(parent);
}
}
} else {
const tree = focused;
const focus = tree.getFocus();
tree.collapse(focus).then(didCollapse => {
if (focus && !didCollapse) {
tree.focusParent({ origin: 'keyboard' });
return tree.reveal(tree.getFocus());
}
return void 0;
});
}
}
}
});
@@ -251,18 +317,39 @@ export function registerCommands(): void {
// Tree only
if (focused && !(focused instanceof List || focused instanceof PagedList)) {
const tree = focused;
const focus = tree.getFocus();
if (focused instanceof ObjectTree || focused instanceof AsyncDataTree) {
const tree = focused;
const focusedElements = tree.getFocus();
tree.expand(focus).then(didExpand => {
if (focus && !didExpand) {
tree.focusFirstChild({ origin: 'keyboard' });
return tree.reveal(tree.getFocus());
if (focusedElements.length === 0) {
return;
}
return void 0;
}).done(null, errors.onUnexpectedError);
const focus = focusedElements[0];
if (!tree.expand(focus)) {
const child = tree.getFirstElementChild(focus);
if (child) {
const fakeKeyboardEvent = new KeyboardEvent('keydown');
tree.setFocus([child], fakeKeyboardEvent);
tree.reveal(child);
}
}
} else {
const tree = focused;
const focus = tree.getFocus();
tree.expand(focus).then(didExpand => {
if (focus && !didExpand) {
tree.focusFirstChild({ origin: 'keyboard' });
return tree.reveal(tree.getFocus());
}
return void 0;
});
}
}
}
});
@@ -286,12 +373,21 @@ export function registerCommands(): void {
list.reveal(list.getFocus()[0]);
}
// ObjectTree
else if (focused instanceof ObjectTree || focused instanceof AsyncDataTree) {
const list = focused;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
list.focusPreviousPage(fakeKeyboardEvent);
list.reveal(list.getFocus()[0]);
}
// Tree
else if (focused) {
const tree = focused;
tree.focusPreviousPage({ origin: 'keyboard' });
tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError);
tree.reveal(tree.getFocus());
}
}
});
@@ -315,12 +411,21 @@ export function registerCommands(): void {
list.reveal(list.getFocus()[0]);
}
// ObjectTree
else if (focused instanceof ObjectTree || focused instanceof AsyncDataTree) {
const list = focused;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
list.focusNextPage(fakeKeyboardEvent);
list.reveal(list.getFocus()[0]);
}
// Tree
else if (focused) {
const tree = focused;
tree.focusNextPage({ origin: 'keyboard' });
tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError);
tree.reveal(tree.getFocus());
}
}
});
@@ -337,7 +442,7 @@ export function registerCommands(): void {
id: 'list.focusFirstChild',
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
primary: null,
primary: 0,
handler: accessor => listFocusFirst(accessor, { fromFocused: true })
});
@@ -355,12 +460,26 @@ export function registerCommands(): void {
list.reveal(0);
}
// ObjectTree
else if (focused instanceof ObjectTree || focused instanceof AsyncDataTree) {
const list = focused;
const first = list.getFirstElementChild(null);
if (!first) {
return;
}
const fakeKeyboardEvent = new KeyboardEvent('keydown');
list.setFocus([first], fakeKeyboardEvent);
list.reveal(first);
}
// Tree
else if (focused) {
const tree = focused;
tree.focusFirst({ origin: 'keyboard' }, options && options.fromFocused ? tree.getFocus() : void 0);
tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError);
tree.reveal(tree.getFocus());
}
}
@@ -376,7 +495,7 @@ export function registerCommands(): void {
id: 'list.focusLastChild',
weight: KeybindingWeight.WorkbenchContrib,
when: WorkbenchListFocusContextKey,
primary: null,
primary: 0,
handler: accessor => listFocusLast(accessor, { fromFocused: true })
});
@@ -394,12 +513,26 @@ export function registerCommands(): void {
list.reveal(list.length - 1);
}
// ObjectTree
else if (focused instanceof ObjectTree || focused instanceof AsyncDataTree) {
const list = focused;
const last = list.getLastElementAncestor();
if (!last) {
return;
}
const fakeKeyboardEvent = new KeyboardEvent('keydown');
list.setFocus([last], fakeKeyboardEvent);
list.reveal(last);
}
// Tree
else if (focused) {
const tree = focused;
tree.focusLast({ origin: 'keyboard' }, options && options.fromFocused ? tree.getFocus() : void 0);
tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError);
tree.reveal(tree.getFocus());
}
}
@@ -422,6 +555,14 @@ export function registerCommands(): void {
list.open(list.getFocus());
}
// ObjectTree
else if (focused instanceof ObjectTree || focused instanceof AsyncDataTree) {
const list = focused;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
list.setSelection(list.getFocus(), fakeKeyboardEvent);
list.open(list.getFocus());
}
// Tree
else if (focused) {
const tree = focused;
@@ -460,11 +601,22 @@ export function registerCommands(): void {
// Tree only
if (focused && !(focused instanceof List || focused instanceof PagedList)) {
const tree = focused;
const focus = tree.getFocus();
if (focused instanceof ObjectTree || focused instanceof AsyncDataTree) {
const tree = focused;
const focus = tree.getFocus();
if (focus) {
tree.toggleExpansion(focus);
if (focus.length === 0) {
return;
}
tree.toggleCollapsed(focus[0]);
} else {
const tree = focused;
const focus = tree.getFocus();
if (focus) {
tree.toggleExpansion(focus);
}
}
}
}
@@ -484,14 +636,20 @@ export function registerCommands(): void {
if (list.getSelection().length > 0) {
list.setSelection([]);
return void 0;
}
if (list.getFocus().length > 0) {
} else if (list.getFocus().length > 0) {
list.setFocus([]);
}
}
return void 0;
// ObjectTree
else if (focused instanceof ObjectTree || focused instanceof AsyncDataTree) {
const list = focused;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
if (list.getSelection().length > 0) {
list.setSelection([], fakeKeyboardEvent);
} else if (list.getFocus().length > 0) {
list.setFocus([], fakeKeyboardEvent);
}
}
@@ -501,14 +659,8 @@ export function registerCommands(): void {
if (tree.getSelection().length) {
tree.clearSelection({ origin: 'keyboard' });
return void 0;
}
if (tree.getFocus()) {
} else if (tree.getFocus()) {
tree.clearFocus({ origin: 'keyboard' });
return void 0;
}
}
}
@@ -550,9 +702,16 @@ export function registerCommands(): void {
win: { primary: void 0 }
});
CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, path: string | ISingleFolderWorkspaceIdentifier) {
CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, path: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string) {
const windowsService = accessor.get(IWindowsService);
return windowsService.removeFromRecentlyOpened([path]).then(() => void 0);
});
CommandsRegistry.registerCommand('_workbench.downloadResource', function (accessor: ServicesAccessor, resource: URI) {
const downloadService = accessor.get(IDownloadService);
const location = posix.join(tmpdir(), generateUuid());
return downloadService.download(resource, location).then(() => URI.file(location));
});
}

View File

@@ -3,8 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Registry } from 'vs/platform/registry/common/platform';
import * as nls from 'vs/nls';
import product from 'vs/platform/node/product';
@@ -14,7 +12,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, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction, OpenRecentAction } 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, ToggleSharedProcessAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, ShowAboutDialogAction, InspectContextKeysAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction, OpenRecentAction } from 'vs/workbench/electron-browser/actions';
import { registerCommands, QUIT_ID } from 'vs/workbench/electron-browser/commands';
import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction } from 'vs/workbench/browser/actions/workspaceActions';
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
@@ -22,6 +20,8 @@ import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browse
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ADD_ROOT_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
import { IsMacContext } from 'vs/platform/workbench/common/contextkeys';
// Contribute Commands
registerCommands();
@@ -33,7 +33,7 @@ const fileCategory = nls.localize('file', "File");
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NewWindowAction, NewWindowAction.ID, NewWindowAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_N }), 'New Window');
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(CloseCurrentWindowAction, CloseCurrentWindowAction.ID, CloseCurrentWindowAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_W }), 'Close Window');
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SwitchWindow, SwitchWindow.ID, SwitchWindow.LABEL, { primary: null, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_W } }), 'Switch Window...');
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SwitchWindow, SwitchWindow.ID, SwitchWindow.LABEL, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_W } }), 'Switch Window...');
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(QuickSwitchWindow, QuickSwitchWindow.ID, QuickSwitchWindow.LABEL), 'Quick Switch Window...');
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenRecentAction, QuickOpenRecentAction.ID, QuickOpenRecentAction.LABEL), 'File: Quick Open Recent...', fileCategory);
@@ -66,7 +66,7 @@ if (OpenTipsAndTricksUrlAction.AVAILABLE) {
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenTipsAndTricksUrlAction, OpenTipsAndTricksUrlAction.ID, OpenTipsAndTricksUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory);
}
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenTwitterUrlAction, OpenTwitterUrlAction.ID, OpenTwitterUrlAction.LABEL), 'Help: Join us on Twitter', helpCategory);
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenTwitterUrlAction, OpenTwitterUrlAction.ID, OpenTwitterUrlAction.LABEL), 'Help: Join Us on Twitter', helpCategory);
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenRequestFeatureUrlAction, OpenRequestFeatureUrlAction.ID, OpenRequestFeatureUrlAction.LABEL), 'Help: Search Feature Requests', helpCategory);
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLicenseUrlAction, OpenLicenseUrlAction.ID, OpenLicenseUrlAction.LABEL), 'Help: View License', helpCategory);
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenPrivacyStatementUrlAction, OpenPrivacyStatementUrlAction.ID, OpenPrivacyStatementUrlAction.LABEL), 'Help: Privacy Statement', helpCategory);
@@ -117,14 +117,13 @@ CommandsRegistry.registerCommand(OpenWorkspaceConfigFileAction.ID, serviceAccess
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: OpenWorkspaceConfigFileAction.ID,
title: `${workspacesCategory}: ${OpenWorkspaceConfigFileAction.LABEL}`,
title: { value: `${workspacesCategory}: ${OpenWorkspaceConfigFileAction.LABEL}`, original: 'Workspaces: Open Workspace Configuration File' },
},
when: new RawContextKey<string>('workbenchState', '').isEqualTo('workspace')
});
// Developer related actions
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);
@@ -162,34 +161,35 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
order: 2
});
if (!isMacintosh) {
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '2_open',
command: {
id: OpenFileAction.ID,
title: nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")
},
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '2_open',
command: {
id: OpenFileAction.ID,
title: nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")
},
order: 1,
when: IsMacContext.toNegated()
});
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '2_open',
command: {
id: OpenFolderAction.ID,
title: nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")
},
order: 2
});
} else {
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '2_open',
command: {
id: OpenFileFolderAction.ID,
title: nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")
},
order: 1
});
}
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '2_open',
command: {
id: OpenFolderAction.ID,
title: nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")
},
order: 2,
when: IsMacContext.toNegated()
});
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '2_open',
command: {
id: OpenFileFolderAction.ID,
title: nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")
},
order: 1,
when: IsMacContext
});
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '2_open',
@@ -221,7 +221,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, {
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '3_workspace',
command: {
id: AddRootFolderAction.ID,
id: ADD_ROOT_FOLDER_COMMAND_ID,
title: nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...")
},
order: 1
@@ -236,14 +236,13 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
order: 2
});
if (!isMacintosh) {
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
title: nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences"),
submenu: MenuId.MenubarPreferencesMenu,
group: '5_autosave',
order: 2
});
}
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
title: nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences"),
submenu: MenuId.MenubarPreferencesMenu,
group: '5_autosave',
order: 2,
when: IsMacContext.toNegated()
});
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '6_close',
@@ -275,16 +274,15 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
order: 4
});
if (!isMacintosh) {
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: 'z_Exit',
command: {
id: QUIT_ID,
title: nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit")
},
order: 1
});
}
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: 'z_Exit',
command: {
id: QUIT_ID,
title: nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit")
},
order: 1,
when: IsMacContext.toNegated()
});
// Appereance menu
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
@@ -309,6 +307,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
id: ToggleMenuBarAction.ID,
title: nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar")
},
when: IsMacContext.toNegated(),
order: 4
});
@@ -341,6 +340,130 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
order: 3
});
// Help
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '1_welcome',
command: {
id: 'workbench.action.openDocumentationUrl',
title: nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation")
},
order: 3
});
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '1_welcome',
command: {
id: 'update.showCurrentReleaseNotes',
title: nls.localize({ key: 'miReleaseNotes', comment: ['&& denotes a mnemonic'] }, "&&Release Notes")
},
order: 4
});
// Reference
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '2_reference',
command: {
id: 'workbench.action.keybindingsReference',
title: nls.localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference")
},
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '2_reference',
command: {
id: 'workbench.action.openIntroductoryVideosUrl',
title: nls.localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos")
},
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '2_reference',
command: {
id: 'workbench.action.openTipsAndTricksUrl',
title: nls.localize({ key: 'miTipsAndTricks', comment: ['&& denotes a mnemonic'] }, "&&Tips and Tricks")
},
order: 3
});
// Feedback
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '3_feedback',
command: {
id: 'workbench.action.openTwitterUrl',
title: nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join Us on Twitter")
},
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '3_feedback',
command: {
id: 'workbench.action.openRequestFeatureUrl',
title: nls.localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests")
},
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '3_feedback',
command: {
id: 'workbench.action.openIssueReporter',
title: nls.localize({ key: 'miReportIssue', comment: ['&& denotes a mnemonic', 'Translate this to "Report Issue in English" in all languages please!'] }, "Report &&Issue")
},
order: 3
});
// Legal
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '4_legal',
command: {
id: 'workbench.action.openLicenseUrl',
title: nls.localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License")
},
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '4_legal',
command: {
id: 'workbench.action.openPrivacyStatementUrl',
title: nls.localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "&&Privacy Statement")
},
order: 2
});
// Tools
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '5_tools',
command: {
id: 'workbench.action.toggleDevTools',
title: nls.localize({ key: 'miToggleDevTools', comment: ['&& denotes a mnemonic'] }, "&&Toggle Developer Tools")
},
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '5_tools',
command: {
id: 'workbench.action.openProcessExplorer',
title: nls.localize({ key: 'miOpenProcessExplorerer', comment: ['&& denotes a mnemonic'] }, "Open &&Process Explorer")
},
order: 2
});
// About
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: 'z_about',
command: {
id: 'workbench.action.showAboutDialog',
title: nls.localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About")
},
order: 1,
when: IsMacContext.toNegated()
});
// Configuration: Workbench
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
@@ -356,14 +479,19 @@ configurationRegistry.registerConfiguration({
'description': nls.localize('showEditorTabs', "Controls whether opened editors should show in tabs or not."),
'default': true
},
'workbench.editor.highlightModifiedTabs': {
'type': 'boolean',
'description': nls.localize('highlightModifiedTabs', "Controls whether a top border is drawn on modified (dirty) editor tabs or not."),
'default': false
},
'workbench.editor.labelFormat': {
'type': 'string',
'enum': ['default', 'short', 'medium', 'long'],
'enumDescriptions': [
nls.localize('workbench.editor.labelFormat.default', "Show the name of the file. When tabs are enabled and two files have the same name in one group the distinguinshing sections of each file's path are added. When tabs are disabled, the path relative to the workspace folder is shown if the editor is active."),
nls.localize('workbench.editor.labelFormat.short', "Show the name of the file followed by it's directory name."),
nls.localize('workbench.editor.labelFormat.medium', "Show the name of the file followed by it's path relative to the workspace folder."),
nls.localize('workbench.editor.labelFormat.long', "Show the name of the file followed by it's absolute path.")
nls.localize('workbench.editor.labelFormat.default', "Show the name of the file. When tabs are enabled and two files have the same name in one group the distinguishing sections of each file's path are added. When tabs are disabled, the path relative to the workspace folder is shown if the editor is active."),
nls.localize('workbench.editor.labelFormat.short', "Show the name of the file followed by its directory name."),
nls.localize('workbench.editor.labelFormat.medium', "Show the name of the file followed by its path relative to the workspace folder."),
nls.localize('workbench.editor.labelFormat.long', "Show the name of the file followed by its absolute path.")
],
'default': 'default',
'description': nls.localize({
@@ -394,30 +522,30 @@ configurationRegistry.registerConfiguration({
},
'workbench.editor.enablePreview': {
'type': 'boolean',
'description': nls.localize('enablePreview', "Controls whether opened editors show as preview. Preview editors are reused until they are kept (e.g. via double click or editing) and show up with an italic font style."),
'description': nls.localize('enablePreview', "Controls whether opened editors show as preview. Preview editors are reused until they are pinned (e.g. via double click or editing) and show up with an italic font style."),
'default': true
},
'workbench.editor.enablePreviewFromQuickOpen': {
'type': 'boolean',
'description': nls.localize('enablePreviewFromQuickOpen', "Controls whether opened editors from Quick Open show as preview. Preview editors are reused until they are kept (e.g. via double click or editing)."),
'description': nls.localize('enablePreviewFromQuickOpen', "Controls whether opened editors from Quick Open show as preview. Preview editors are reused until they are pinned (e.g. via double click or editing)."),
'default': true
},
'workbench.editor.closeOnFileDelete': {
'type': 'boolean',
'description': nls.localize('closeOnFileDelete', "Controls whether editors showing a file should close automatically when the file is deleted or renamed by some other process. Disabling this will keep the editor open as dirty on such an event. Note that deleting from within the application will always close the editor and that dirty files will never close to preserve your data."),
'default': true
'description': nls.localize('closeOnFileDelete', "Controls whether editors showing a file that was opened during the session should close automatically when getting deleted or renamed by some other process. Disabling this will keep the editor open on such an event. Note that deleting from within the application will always close the editor and that dirty files will never close to preserve your data."),
'default': false
},
'workbench.editor.openPositioning': {
'type': 'string',
'enum': ['left', 'right', 'first', 'last'],
'default': 'right',
'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorOpenPositioning' }, "Controls where editors open. Select `left` or `right` to open editors to the left or right of the currently active one. Select `first` or `last` to open editors independently from the currently active one.")
'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorOpenPositioning' }, "Controls where editors open. Select `left` or `right` to open editors to the left or right of the currently active one. Select `first` or `last` to open editors independently from the currently active one.")
},
'workbench.editor.openSideBySideDirection': {
'type': 'string',
'enum': ['right', 'down'],
'default': 'right',
'description': nls.localize('sideBySideDirection', "Controls the default direction of editors that are opened side by side (e.g. from the explorer). By default, editors will open on the right hand side of the currently active one. If changed to `down`, the editors will open below the currently active one.")
'markdownDescription': nls.localize('sideBySideDirection', "Controls the default direction of editors that are opened side by side (e.g. from the explorer). By default, editors will open on the right hand side of the currently active one. If changed to `down`, the editors will open below the currently active one.")
},
'workbench.editor.closeEmptyGroups': {
'type': 'boolean',
@@ -435,6 +563,16 @@ configurationRegistry.registerConfiguration({
'default': false,
'included': isMacintosh
},
'workbench.editor.restoreViewState': {
'type': 'boolean',
'description': nls.localize('restoreViewState', "Restores the last view state (e.g. scroll position) when re-opening files after they have been closed."),
'default': true,
},
'workbench.editor.centeredLayoutAutoResize': {
'type': 'boolean',
'default': true,
'description': nls.localize('centeredLayoutAutoResize', "Controls if the centered layout should automatically resize to maximum width when more than one group is open. Once only one group is open it will resize back to the original centered width.")
},
'workbench.commandPalette.history': {
'type': 'number',
'description': nls.localize('commandHistory', "Controls the number of recently used commands to keep in history for the command palette. Set to 0 to disable command history."),
@@ -450,6 +588,11 @@ configurationRegistry.registerConfiguration({
'description': nls.localize('closeOnFocusLost', "Controls whether Quick Open should close automatically once it loses focus."),
'default': true
},
'workbench.quickOpen.preserveInput': {
'type': 'boolean',
'description': nls.localize('workbench.quickOpen.preserveInput', "Controls whether the last typed input to Quick Open should be restored when opening it the next time."),
'default': false
},
'workbench.settings.openDefaultSettings': {
'type': 'boolean',
'description': nls.localize('openDefaultSettings', "Controls whether opening settings also opens an editor showing all default settings."),
@@ -503,22 +646,31 @@ configurationRegistry.registerConfiguration({
},
'workbench.settings.enableNaturalLanguageSearch': {
'type': 'boolean',
'description': nls.localize('enableNaturalLanguageSettingsSearch', "Controls whether to enable the natural language search mode for settings. The natural language search is provided by an online service."),
'description': nls.localize('enableNaturalLanguageSettingsSearch', "Controls whether to enable the natural language search mode for settings. The natural language search is provided by a Microsoft online service."),
'default': true,
'scope': ConfigurationScope.WINDOW,
'tags': ['usesOnlineServices']
},
'workbench.settings.settingsSearchTocBehavior': {
'type': 'string',
'enum': ['hide', 'filter', 'show'],
'enum': ['hide', 'filter'],
'enumDescriptions': [
nls.localize('settingsSearchTocBehavior.hide', "Hide the Table of Contents while searching."),
nls.localize('settingsSearchTocBehavior.filter', "Filter the Table of Contents to just categories that have matching settings. Clicking a category will filter the results to that category."),
],
'description': nls.localize('settingsSearchTocBehavior', "Controls the behavior of the settings editor Table of Contents while searching."),
'default': 'filter',
'scope': ConfigurationScope.WINDOW
},
'workbench.settings.tocVisible': {
'type': 'boolean',
'description': nls.localize('settingsTocVisible', "Controls whether the settings editor Table of Contents is visible."),
'default': true,
'workbench.settings.editor': {
'type': 'string',
'enum': ['ui', 'json'],
'enumDescriptions': [
nls.localize('settings.editor.ui', "Use the settings UI editor."),
nls.localize('settings.editor.json', "Use the JSON file editor."),
],
'description': nls.localize('settings.editor.desc', "Determines which settings editor to use by default."),
'default': 'ui',
'scope': ConfigurationScope.WINDOW
},
'workbench.enableExperiments': {
@@ -526,6 +678,12 @@ configurationRegistry.registerConfiguration({
'description': nls.localize('workbench.enableExperiments', "Fetches experiments to run from a Microsoft online service."),
'default': true,
'tags': ['usesOnlineServices']
},
//TODO@Ben remove ('enableLegacyStorage') after a while
'workbench.enableLegacyStorage': {
'type': 'boolean',
'description': nls.localize('workbench.enableLegacyStorage', "Switches back to the previous storage implementation. Only change this setting if advised to do so."),
'default': false
}
}
});
@@ -550,10 +708,10 @@ configurationRegistry.registerConfiguration({
],
'default': 'off',
'scope': ConfigurationScope.APPLICATION,
'description':
'markdownDescription':
isMacintosh ?
nls.localize('openFilesInNewWindowMac', "Controls whether files should open in a new window.\nNote that there can still be cases where this setting is ignored (e.g. when using the -new-window or -reuse-window command line option).") :
nls.localize('openFilesInNewWindow', "Controls whether files should open in a new window.\nNote that there can still be cases where this setting is ignored (e.g. when using the -new-window or -reuse-window command line option).")
nls.localize('openFilesInNewWindowMac', "Controls whether files should open in a new window. \nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") :
nls.localize('openFilesInNewWindow', "Controls whether files should open in a new window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).")
},
'window.openFoldersInNewWindow': {
'type': 'string',
@@ -565,7 +723,7 @@ configurationRegistry.registerConfiguration({
],
'default': 'default',
'scope': ConfigurationScope.APPLICATION,
'description': nls.localize('openFoldersInNewWindow', "Controls whether folders should open in a new window or replace the last active window.\nNote that there can still be cases where this setting is ignored (e.g. when using the -new-window or -reuse-window command line option).")
'markdownDescription': nls.localize('openFoldersInNewWindow', "Controls whether folders should open in a new window or replace the last active window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).")
},
'window.openWithoutArgumentsInNewWindow': {
'type': 'string',
@@ -576,7 +734,7 @@ configurationRegistry.registerConfiguration({
],
'default': isMacintosh ? 'off' : 'on',
'scope': ConfigurationScope.APPLICATION,
'description': nls.localize('openWithoutArgumentsInNewWindow', "Controls whether a new empty window should open when starting a second instance without arguments or if the last running instance should get focus.\nNote that there can still be cases where this setting is ignored (e.g. when using the -new-window or -reuse-window command line option).")
'markdownDescription': nls.localize('openWithoutArgumentsInNewWindow', "Controls whether a new empty window should open when starting a second instance without arguments or if the last running instance should get focus.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).")
},
'window.restoreWindows': {
'type': 'string',
@@ -605,7 +763,7 @@ configurationRegistry.registerConfiguration({
'window.title': {
'type': 'string',
'default': isMacintosh ? '${activeEditorShort}${separator}${rootName}' : '${dirty}${activeEditorShort}${separator}${rootName}${separator}${appName}',
'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], key: 'title' },
'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], key: 'title' },
"Controls the window title based on the active editor. Variables are substituted based on the context:\n- `\${activeEditorShort}`: the file name (e.g. myFile.txt).\n- `\${activeEditorMedium}`: the path of the file relative to the workspace folder (e.g. myFolder/myFile.txt).\n- `\${activeEditorLong}`: the full path of the file (e.g. /Users/Development/myProject/myFolder/myFile.txt).\n- `\${folderName}`: name of the workspace folder the file is contained in (e.g. myFolder).\n- `\${folderPath}`: file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder).\n- `\${rootName}`: name of the workspace (e.g. myFolder or myWorkspace).\n- `\${rootPath}`: file path of the workspace (e.g. /Users/Development/myWorkspace).\n- `\${appName}`: e.g. VS Code.\n- `\${dirty}`: a dirty indicator if the active editor is dirty.\n- `\${separator}`: a conditional separator (\" - \") that only shows when surrounded by variables with values or static text.")
},
'window.newWindowDimensions': {
@@ -656,7 +814,7 @@ configurationRegistry.registerConfiguration({
'window.titleBarStyle': {
'type': 'string',
'enum': ['native', 'custom'],
'default': isMacintosh ? 'custom' : 'native',
'default': 'custom',
'scope': ConfigurationScope.APPLICATION,
'description': nls.localize('titleBarStyle', "Adjust the appearance of the window title bar. Changes require a full restart to apply.")
},
@@ -667,11 +825,17 @@ configurationRegistry.registerConfiguration({
'description': nls.localize('window.nativeTabs', "Enables macOS Sierra window tabs. Note that changes require a full restart to apply and that native tabs will disable a custom title bar style if configured."),
'included': isMacintosh && parseFloat(os.release()) >= 16 // Minimum: macOS Sierra (10.12.x = darwin 16.x)
},
'window.smoothScrollingWorkaround': {
'window.nativeFullScreen': {
'type': 'boolean',
'default': true,
'description': nls.localize('window.nativeFullScreen', "Controls if native full-screen should be used on macOS. Disable this option to prevent macOS from creating a new space when going full-screen."),
'included': isMacintosh
},
'window.smoothScrollingWorkaround': { // TODO@Ben remove once https://github.com/Microsoft/vscode/issues/61824 settles
'type': 'boolean',
'default': false,
'scope': ConfigurationScope.APPLICATION,
'description': nls.localize('window.smoothScrollingWorkaround', "Enable this workaround if scrolling is no longer smooth after restoring a minimized VS Code window. This is a workaround for an issue (https://github.com/Microsoft/vscode/issues/13612) where scrolling starts to lag on devices with precision trackpads like the Surface devices from Microsoft. Enabling this workaround can result in a little bit of layout flickering after restoring the window from minimized state but is otherwise harmless. Note: in order for this workaround to function, make sure to also set `#window.titleBarStyle#` to `native`."),
'markdownDescription': nls.localize('window.smoothScrollingWorkaround', "Enable this workaround if scrolling is no longer smooth after restoring a minimized VS Code window. This is a workaround for an issue (https://github.com/Microsoft/vscode/issues/13612) where scrolling starts to lag on devices with precision trackpads like the Surface devices from Microsoft. Enabling this workaround can result in a little bit of layout flickering after restoring the window from minimized state but is otherwise harmless."),
'included': isWindows
},
'window.clickThroughInactive': {

View File

@@ -3,165 +3,257 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import * as perf from 'vs/base/common/performance';
import { TPromise } from 'vs/base/common/winjs.base';
import { WorkbenchShell } from 'vs/workbench/electron-browser/shell';
import * as browser from 'vs/base/browser/browser';
import { domContentLoaded } from 'vs/base/browser/dom';
import * as errors from 'vs/base/common/errors';
import { onUnexpectedError } from 'vs/base/common/errors';
import * as comparer from 'vs/base/common/comparers';
import * as platform from 'vs/base/common/platform';
import * as paths from 'vs/base/common/paths';
import uri from 'vs/base/common/uri';
import * as strings from 'vs/base/common/strings';
import { URI as uri } from 'vs/base/common/uri';
import { IWorkspaceContextService, Workspace, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { realpath } from 'vs/base/node/pfs';
import { stat } from 'vs/base/node/pfs';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import * as gracefulFs from 'graceful-fs';
import { IInitData } from 'vs/workbench/services/timer/common/timerService';
import { TimerService } from 'vs/workbench/services/timer/node/timerService';
import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService';
import { IWindowConfiguration, IWindowsService } from 'vs/platform/windows/common/windows';
import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { WindowsChannelClient } from 'vs/platform/windows/node/windowsIpc';
import { IStorageLegacyService, StorageLegacyService, inMemoryLocalStorageInstance, IStorageLegacy } from 'vs/platform/storage/common/storageLegacyService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { StorageService, inMemoryLocalStorageInstance, IStorage } from 'vs/platform/storage/common/storageService';
import { Client as ElectronIPCClient } from 'vs/base/parts/ipc/electron-browser/ipc.electron-browser';
import { webFrame } from 'electron';
import { UpdateChannelClient } from 'vs/platform/update/common/updateIpc';
import { UpdateChannelClient } from 'vs/platform/update/node/updateIpc';
import { IUpdateService } from 'vs/platform/update/common/update';
import { URLHandlerChannel, URLServiceChannelClient } from 'vs/platform/url/common/urlIpc';
import { URLHandlerChannel, URLServiceChannelClient } from 'vs/platform/url/node/urlIpc';
import { IURLService } from 'vs/platform/url/common/url';
import { WorkspacesChannelClient } from 'vs/platform/workspaces/common/workspacesIpc';
import { IWorkspacesService, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { WorkspacesChannelClient } from 'vs/platform/workspaces/node/workspacesIpc';
import { IWorkspacesService, ISingleFolderWorkspaceIdentifier, IWorkspaceInitializationPayload, IMultiFolderWorkspaceInitializationPayload, IEmptyWorkspaceInitializationPayload, ISingleFolderWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces';
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
import * as fs from 'fs';
import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log';
import { IssueChannelClient } from 'vs/platform/issue/common/issueIpc';
import { StorageService, DelegatingStorageService } from 'vs/platform/storage/node/storageService';
import { IssueChannelClient } from 'vs/platform/issue/node/issueIpc';
import { IIssueService } from 'vs/platform/issue/common/issue';
import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc';
import { RelayURLService } from 'vs/platform/url/common/urlService';
import { MenubarChannelClient } from 'vs/platform/menubar/common/menubarIpc';
import { MenubarChannelClient } from 'vs/platform/menubar/node/menubarIpc';
import { IMenubarService } from 'vs/platform/menubar/common/menubar';
import { Schemas } from 'vs/base/common/network';
import { sanitizeFilePath } from 'vs/base/node/extfs';
import { basename } from 'path';
import { createHash } from 'crypto';
import { IdleValue } from 'vs/base/common/async';
import { setGlobalLeakWarningThreshold } from 'vs/base/common/event';
import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc';
gracefulFs.gracefulify(fs); // enable gracefulFs
export function startup(configuration: IWindowConfiguration): TPromise<void> {
export function startup(configuration: IWindowConfiguration): Promise<void> {
// Ensure others can listen to zoom level changes
browser.setZoomFactor(webFrame.getZoomFactor());
// Massage configuration file URIs
revive(configuration);
// See https://github.com/Microsoft/vscode/issues/26151
// Can be trusted because we are not setting it ourselves.
browser.setZoomLevel(webFrame.getZoomLevel(), true /* isTrusted */);
// Setup perf
perf.importEntries(configuration.perfEntries);
// Configure emitter leak warning threshold
setGlobalLeakWarningThreshold(-1);
// Browser config
browser.setZoomFactor(webFrame.getZoomFactor()); // Ensure others can listen to zoom level changes
browser.setZoomLevel(webFrame.getZoomLevel(), true /* isTrusted */); // Can be trusted because we are not setting it ourselves (https://github.com/Microsoft/vscode/issues/26151)
browser.setFullscreen(!!configuration.fullscreen);
KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged();
browser.setAccessibilitySupport(configuration.accessibilitySupport ? platform.AccessibilitySupport.Enabled : platform.AccessibilitySupport.Disabled);
// Setup Intl
comparer.setFileNameComparer(new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }));
// Keyboard support
KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged();
// Setup Intl for comparers
comparer.setFileNameComparer(new IdleValue(() => {
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
return {
collator: collator,
collatorIsNumeric: collator.resolvedOptions().numeric
};
}));
// Open workbench
return openWorkbench(configuration);
}
function openWorkbench(configuration: IWindowConfiguration): TPromise<void> {
function revive(workbench: IWindowConfiguration) {
if (workbench.folderUri) {
workbench.folderUri = uri.revive(workbench.folderUri);
}
const filesToWaitPaths = workbench.filesToWait && workbench.filesToWait.paths;
[filesToWaitPaths, workbench.filesToOpen, workbench.filesToCreate, workbench.filesToDiff].forEach(paths => {
if (Array.isArray(paths)) {
paths.forEach(path => {
if (path.fileUri) {
path.fileUri = uri.revive(path.fileUri);
}
});
}
});
}
function openWorkbench(configuration: IWindowConfiguration): Promise<void> {
const mainProcessClient = new ElectronIPCClient(`window:${configuration.windowId}`);
const mainServices = createMainProcessServices(mainProcessClient, configuration);
const mainServices = createMainProcessServices(mainProcessClient);
const environmentService = new EnvironmentService(configuration, configuration.execPath);
const logService = createLogService(mainProcessClient, configuration, environmentService);
logService.trace('openWorkbench configuration', JSON.stringify(configuration));
// Since the configuration service is one of the core services that is used in so many places, we initialize it
// right before startup of the workbench shell to have its data ready for consumers
return createAndInitializeWorkspaceService(configuration, environmentService).then(workspaceService => {
const timerService = new TimerService((<any>window).MonacoEnvironment.timers as IInitData, workspaceService.getWorkbenchState() === WorkbenchState.EMPTY);
const storageService = createStorageService(workspaceService, environmentService);
// Resolve a workspace payload that we can get the workspace ID from
return createWorkspaceInitializationPayload(configuration, environmentService).then(payload => {
return domContentLoaded().then(() => {
return Promise.all([
// Open Shell
perf.mark('willStartWorkbench');
const shell = new WorkbenchShell(document.body, {
contextService: workspaceService,
configurationService: workspaceService,
environmentService,
logService,
timerService,
storageService
}, mainServices, mainProcessClient, configuration);
shell.open();
// Create and initialize workspace/configuration service
createWorkspaceService(payload, environmentService, logService),
// Inform user about loading issues from the loader
(<any>self).require.config({
onError: (err: any) => {
if (err.errorCode === 'load') {
shell.onUnexpectedError(loaderError(err));
// Create and initialize storage service
createStorageService(payload, environmentService, logService, mainProcessClient)
]).then(services => {
const workspaceService = services[0];
const storageService = new DelegatingStorageService(services[1], createStorageLegacyService(workspaceService, environmentService), logService, workspaceService);
return domContentLoaded().then(() => {
perf.mark('willStartWorkbench');
// Create Shell
const shell = new WorkbenchShell(document.body, {
contextService: workspaceService,
configurationService: workspaceService,
environmentService,
logService,
storageService
}, mainServices, mainProcessClient, configuration);
// Gracefully Shutdown Storage
shell.onWillShutdown(event => {
event.join(storageService.close());
});
// Open Shell
shell.open();
// Inform user about loading issues from the loader
(<any>self).require.config({
onError: err => {
if (err.errorCode === 'load') {
shell.onUnexpectedError(new Error(nls.localize('loaderErrorNative', "Failed to load a required file. Please restart the application to try again. Details: {0}", JSON.stringify(err))));
}
}
}
});
});
});
});
}
function createAndInitializeWorkspaceService(configuration: IWindowConfiguration, environmentService: EnvironmentService): TPromise<WorkspaceService> {
const folderUri = configuration.folderUri ? uri.revive(configuration.folderUri) : null;
return validateFolderUri(folderUri, configuration.verbose).then(validatedFolderUri => {
function createWorkspaceInitializationPayload(configuration: IWindowConfiguration, environmentService: EnvironmentService): Promise<IWorkspaceInitializationPayload> {
const workspaceService = new WorkspaceService(environmentService);
return workspaceService.initialize(configuration.workspace || validatedFolderUri || configuration).then(() => workspaceService, error => workspaceService);
});
}
function validateFolderUri(folderUri: ISingleFolderWorkspaceIdentifier, verbose: boolean): TPromise<uri> {
// Return early if we do not have a single folder uri or if it is a non file uri
if (!folderUri || folderUri.scheme !== Schemas.file) {
return TPromise.as(folderUri);
// Multi-root workspace
if (configuration.workspace) {
return Promise.resolve(configuration.workspace as IMultiFolderWorkspaceInitializationPayload);
}
// Otherwise: use realpath to resolve symbolic links to the truth
return realpath(folderUri.fsPath).then(realFolderPath => {
// Single-folder workspace
let workspaceInitializationPayload: Promise<IWorkspaceInitializationPayload> = Promise.resolve(void 0);
if (configuration.folderUri) {
workspaceInitializationPayload = resolveSingleFolderWorkspaceInitializationPayload(configuration.folderUri);
}
// For some weird reason, node adds a trailing slash to UNC paths
// we never ever want trailing slashes as our workspace path unless
// someone opens root ("/").
// See also https://github.com/nodejs/io.js/issues/1765
if (paths.isUNC(realFolderPath) && strings.endsWith(realFolderPath, paths.nativeSep)) {
realFolderPath = strings.rtrim(realFolderPath, paths.nativeSep);
return workspaceInitializationPayload.then(payload => {
// Fallback to empty workspace if we have no payload yet.
if (!payload) {
let id: string;
if (configuration.backupPath) {
id = basename(configuration.backupPath); // we know the backupPath must be a unique path so we leverage its name as workspace ID
} else if (environmentService.isExtensionDevelopment) {
id = 'ext-dev'; // extension development window never stores backups and is a singleton
} else {
return Promise.reject(new Error('Unexpected window configuration without backupPath'));
}
payload = { id } as IEmptyWorkspaceInitializationPayload;
}
return uri.file(realFolderPath);
}, error => {
if (verbose) {
errors.onUnexpectedError(error);
}
// Treat any error case as empty workbench case (no folder path)
return null;
}).then(realFolderUriOrNull => {
// Update config with real path if we have one
return realFolderUriOrNull;
return payload;
});
}
function createStorageService(workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): IStorageService {
function resolveSingleFolderWorkspaceInitializationPayload(folderUri: ISingleFolderWorkspaceIdentifier): Promise<ISingleFolderWorkspaceInitializationPayload> {
// Return early the folder is not local
if (folderUri.scheme !== Schemas.file) {
return Promise.resolve({ id: createHash('md5').update(folderUri.toString()).digest('hex'), folder: folderUri });
}
function computeLocalDiskFolderId(folder: uri, stat: fs.Stats): string {
let ctime: number;
if (platform.isLinux) {
ctime = stat.ino; // Linux: birthtime is ctime, so we cannot use it! We use the ino instead!
} else if (platform.isMacintosh) {
ctime = stat.birthtime.getTime(); // macOS: birthtime is fine to use as is
} else if (platform.isWindows) {
if (typeof stat.birthtimeMs === 'number') {
ctime = Math.floor(stat.birthtimeMs); // Windows: fix precision issue in node.js 8.x to get 7.x results (see https://github.com/nodejs/node/issues/19897)
} else {
ctime = stat.birthtime.getTime();
}
}
// we use the ctime as extra salt to the ID so that we catch the case of a folder getting
// deleted and recreated. in that case we do not want to carry over previous state
return createHash('md5').update(folder.fsPath).update(ctime ? String(ctime) : '').digest('hex');
}
// For local: ensure path is absolute and exists
const sanitizedFolderPath = sanitizeFilePath(folderUri.fsPath, process.env['VSCODE_CWD'] || process.cwd());
return stat(sanitizedFolderPath).then(stat => {
const sanitizedFolderUri = uri.file(sanitizedFolderPath);
return {
id: computeLocalDiskFolderId(sanitizedFolderUri, stat),
folder: sanitizedFolderUri
} as ISingleFolderWorkspaceInitializationPayload;
}, error => onUnexpectedError(error));
}
function createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService, logService: ILogService): Promise<WorkspaceService> {
const workspaceService = new WorkspaceService(environmentService);
return workspaceService.initialize(payload).then(() => workspaceService, error => {
onUnexpectedError(error);
logService.error(error);
return workspaceService;
});
}
function createStorageService(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService, logService: ILogService, mainProcessClient: ElectronIPCClient): Thenable<StorageService> {
const globalStorageDatabase = new GlobalStorageDatabaseChannelClient(mainProcessClient.getChannel('storage'));
const storageService = new StorageService(globalStorageDatabase, logService, environmentService);
return storageService.initialize(payload).then(() => storageService, error => {
onUnexpectedError(error);
logService.error(error);
return storageService;
});
}
function createStorageLegacyService(workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): IStorageLegacyService {
let workspaceId: string;
let secondaryWorkspaceId: number;
switch (workspaceService.getWorkbenchState()) {
@@ -175,10 +267,9 @@ function createStorageService(workspaceService: IWorkspaceContextService, enviro
case WorkbenchState.FOLDER:
const workspace: Workspace = <Workspace>workspaceService.getWorkspace();
workspaceId = workspace.folders[0].uri.toString();
secondaryWorkspaceId = workspace.ctime;
break;
// finaly, if we do not have a workspace open, we need to find another identifier for the window to store
// finally, if we do not have a workspace open, we need to find another identifier for the window to store
// workspace UI state. if we have a backup path in the configuration we can use that because this
// will be a unique identifier per window that is stable between restarts as long as there are
// dirty files in the workspace.
@@ -191,14 +282,14 @@ function createStorageService(workspaceService: IWorkspaceContextService, enviro
const disableStorage = !!environmentService.extensionTestsPath; // never keep any state when running extension tests!
let storage: IStorage;
let storage: IStorageLegacy;
if (disableStorage) {
storage = inMemoryLocalStorageInstance;
} else {
storage = window.localStorage;
}
return new StorageService(storage, storage, workspaceId, secondaryWorkspaceId);
return new StorageLegacyService(storage, storage, workspaceId);
}
function createLogService(mainProcessClient: ElectronIPCClient, configuration: IWindowConfiguration, environmentService: IEnvironmentService): ILogService {
@@ -206,17 +297,18 @@ function createLogService(mainProcessClient: ElectronIPCClient, configuration: I
const consoleLogService = new ConsoleLogService(configuration.logLevel);
const logService = new MultiplexLogService([consoleLogService, spdlogService]);
const logLevelClient = new LogLevelSetterChannelClient(mainProcessClient.getChannel('loglevel'));
return new FollowerLogService(logLevelClient, logService);
}
function createMainProcessServices(mainProcessClient: ElectronIPCClient, configuration: IWindowConfiguration): ServiceCollection {
function createMainProcessServices(mainProcessClient: ElectronIPCClient): ServiceCollection {
const serviceCollection = new ServiceCollection();
const windowsChannel = mainProcessClient.getChannel('windows');
serviceCollection.set(IWindowsService, new WindowsChannelClient(windowsChannel));
const updateChannel = mainProcessClient.getChannel('update');
serviceCollection.set(IUpdateService, new SyncDescriptor(UpdateChannelClient, updateChannel));
serviceCollection.set(IUpdateService, new SyncDescriptor(UpdateChannelClient, [updateChannel]));
const urlChannel = mainProcessClient.getChannel('url');
const mainUrlService = new URLServiceChannelClient(urlChannel);
@@ -227,21 +319,13 @@ function createMainProcessServices(mainProcessClient: ElectronIPCClient, configu
mainProcessClient.registerChannel('urlHandler', urlHandlerChannel);
const issueChannel = mainProcessClient.getChannel('issue');
serviceCollection.set(IIssueService, new SyncDescriptor(IssueChannelClient, issueChannel));
serviceCollection.set(IIssueService, new SyncDescriptor(IssueChannelClient, [issueChannel]));
const menubarChannel = mainProcessClient.getChannel('menubar');
serviceCollection.set(IMenubarService, new SyncDescriptor(MenubarChannelClient, menubarChannel));
serviceCollection.set(IMenubarService, new SyncDescriptor(MenubarChannelClient, [menubarChannel]));
const workspacesChannel = mainProcessClient.getChannel('workspaces');
serviceCollection.set(IWorkspacesService, new WorkspacesChannelClient(workspacesChannel));
return serviceCollection;
}
function loaderError(err: Error): Error {
if (platform.isWeb) {
return new Error(nls.localize('loaderError', "Failed to load a required file. Either you are no longer connected to the internet or the server you are connected to is offline. Please refresh the browser to try again."));
}
return new Error(nls.localize('loaderErrorNative', "Failed to load a required file. Please restart the application to try again. Details: {0}", JSON.stringify(err)));
}

View File

@@ -15,11 +15,16 @@
/* Font Families (with CJK support) */
.monaco-shell { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; }
.monaco-shell:lang(zh-Hans) { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; }
.monaco-shell:lang(zh-Hant) { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Microsoft Jhenghei", "PingFang TC", "Source Han Sans TC", "Source Han Sans", "Source Han Sans TW", sans-serif; }
.monaco-shell:lang(ja) { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Meiryo", "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif; }
.monaco-shell:lang(ko) { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Malgun Gothic", "Nanum Gothic", "Dotom", "Apple SD Gothic Neo", "AppleGothic", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; }
.monaco-shell,
.monaco-shell .monaco-menu-container .monaco-menu { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; }
.monaco-shell:lang(zh-Hans),
.monaco-shell:lang(zh-Hans) .monaco-menu-container .monaco-menu { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; }
.monaco-shell:lang(zh-Hant),
.monaco-shell:lang(zh-Hant) .monaco-menu-container .monaco-menu { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Microsoft Jhenghei", "PingFang TC", "Source Han Sans TC", "Source Han Sans", "Source Han Sans TW", sans-serif; }
.monaco-shell:lang(ja),
.monaco-shell:lang(ja) .monaco-menu-container .monaco-menu { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Meiryo", "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif; }
.monaco-shell:lang(ko),
.monaco-shell:lang(ko) .monaco-menu-container .monaco-menu { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Malgun Gothic", "Nanum Gothic", "Dotom", "Apple SD Gothic Neo", "AppleGothic", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@@ -65,8 +70,8 @@
cursor: pointer;
}
.monaco-shell .monaco-menu-container .monaco-menu {
font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif;
.monaco-shell .context-view {
-webkit-app-region: no-drag;
}
.monaco-shell .monaco-menu .monaco-action-bar.vertical {
@@ -79,16 +84,30 @@
.monaco-shell .monaco-menu .monaco-action-bar.vertical .action-label:not(.separator),
.monaco-shell .monaco-menu .monaco-action-bar.vertical .keybinding {
padding: 0 1.5em;
font-size: inherit;
padding: 0 2em;
}
.monaco-shell .monaco-menu .monaco-action-bar.vertical .menu-item-check {
font-size: inherit;
width: 2em;
}
.monaco-shell .monaco-menu .monaco-action-bar.vertical .action-label.separator {
font-size: inherit;
padding: 0.2em 0 0 0;
margin-bottom: 0.2em;
}
.monaco-shell .monaco-menu .monaco-action-bar.vertical .submenu-indicator {
padding: 0 1em;
font-size: 60%;
padding: 0 1.8em;
}
.monaco-shell .linux .monaco-menu .monaco-action-bar.vertical .submenu-indicator {
height: 100%;
-webkit-mask-size: 10px 10px;
mask-size: 10px 10px;
}
.monaco-shell .monaco-menu .action-item {
@@ -120,7 +139,7 @@
}
.monaco-shell .mac select:focus {
border: none; /* outline is a square, but border has a radius, so we avoid this glitch when focused (https://github.com/Microsoft/vscode/issues/26045) */
border-color: transparent; /* outline is a square, but border has a radius, so we avoid this glitch when focused (https://github.com/Microsoft/vscode/issues/26045) */
}
.monaco-shell .monaco-tree.focused .monaco-tree-row.focused [tabindex="0"]:focus {

View File

@@ -3,9 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import * as objects from 'vs/base/common/objects';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { Disposable } from 'vs/base/common/lifecycle';
@@ -17,7 +15,7 @@ import { normalize } from 'vs/base/common/paths';
export class ResourceGlobMatcher extends Disposable {
private static readonly NO_ROOT: string = null;
private static readonly NO_ROOT: string | null = null;
private readonly _onExpressionChange: Emitter<void> = this._register(new Emitter<void>());
get onExpressionChange(): Event<void> { return this._onExpressionChange.event; }

View File

@@ -3,14 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./media/shell';
import * as platform from 'vs/base/common/platform';
import * as perf from 'vs/base/common/performance';
import * as aria from 'vs/base/browser/ui/aria/aria';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { Disposable } from 'vs/base/common/lifecycle';
import * as errors from 'vs/base/common/errors';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import product from 'vs/platform/node/product';
@@ -19,8 +17,8 @@ import pkg from 'vs/platform/node/package';
import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService';
import { Workbench, IWorkbenchStartedInfo } from 'vs/workbench/electron-browser/workbench';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService, configurationTelemetry, LogAppender, combinedAppender } from 'vs/platform/telemetry/common/telemetryUtils';
import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc';
import { NullTelemetryService, configurationTelemetry, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils';
import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc';
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry';
import { ElectronWindow } from 'vs/workbench/electron-browser/window';
@@ -42,11 +40,10 @@ import { IIntegrityService } from 'vs/platform/integrity/common/integrity';
import { EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { ExtensionService } from 'vs/workbench/services/extensions/electron-browser/extensionService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { ILifecycleService, LifecyclePhase, ShutdownReason, StartupKind } from 'vs/platform/lifecycle/common/lifecycle';
import { InstantiationService } from 'vs/platform/instantiation/node/instantiationService';
import { ILifecycleService, LifecyclePhase, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ISearchService, ISearchHistoryService } from 'vs/platform/search/common/search';
@@ -58,19 +55,17 @@ import { WorkbenchModeServiceImpl } from 'vs/workbench/services/mode/common/work
import { IModeService } from 'vs/editor/common/services/modeService';
import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { ICrashReporterService, NullCrashReporterService, CrashReporterService } from 'vs/workbench/services/crashReporter/electron-browser/crashReporterService';
import { getDelayedChannel, IPCClient } from 'vs/base/parts/ipc/common/ipc';
import { getDelayedChannel, IPCClient } from 'vs/base/parts/ipc/node/ipc';
import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net';
import { DefaultURITransformer } from 'vs/base/common/uriIpc';
import { IExtensionManagementChannel, ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
import { IExtensionManagementService, IExtensionEnablementService, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/node/extensionManagementIpc';
import { IExtensionManagementService, IExtensionEnablementService, IExtensionManagementServerService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService';
import { ITimerService } from 'vs/workbench/services/timer/common/timerService';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { restoreFontInfo, readFontInfo, saveFontInfo } from 'vs/editor/browser/config/configuration';
import * as browser from 'vs/base/browser/browser';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { WorkbenchThemeService } from 'vs/workbench/services/themes/electron-browser/workbenchThemeService';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration';
import { TextResourceConfigurationService } from 'vs/editor/common/services/resourceConfigurationImpl';
import { registerThemingParticipant, ITheme, ICssStyleCollector, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
import { foreground, selectionBackground, focusBorder, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, listHighlightForeground, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry';
@@ -80,10 +75,11 @@ import { IBroadcastService, BroadcastService } from 'vs/platform/broadcast/elect
import { HashService } from 'vs/workbench/services/hash/node/hashService';
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
import { ILogService } from 'vs/platform/log/common/log';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { DelegatingStorageService } from 'vs/platform/storage/node/storageService';
import { Event, Emitter } from 'vs/base/common/event';
import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme';
import { stat } from 'fs';
import { join } from 'path';
import { ILocalizationsChannel, LocalizationsChannelClient } from 'vs/platform/localizations/common/localizationsIpc';
import { LocalizationsChannelClient } from 'vs/platform/localizations/node/localizationsIpc';
import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
import { WorkbenchIssueService } from 'vs/workbench/services/issue/electron-browser/workbenchIssueService';
@@ -91,13 +87,24 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { NotificationService } from 'vs/workbench/services/notification/common/notificationService';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { DialogService } from 'vs/workbench/services/dialogs/electron-browser/dialogService';
import { DialogChannel } from 'vs/platform/dialogs/common/dialogIpc';
import { EventType, addDisposableListener, addClass } from 'vs/base/browser/dom';
import { DialogChannel } from 'vs/platform/dialogs/node/dialogIpc';
import { EventType, addDisposableListener, addClass, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService';
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { OpenerService } from 'vs/editor/browser/services/openerService';
import { SearchHistoryService } from 'vs/workbench/services/search/node/searchHistoryService';
import { MulitExtensionManagementService } from 'vs/platform/extensionManagement/common/multiExtensionManagement';
import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
import { LogLevelSetterChannel } from 'vs/platform/log/node/logIpc';
import { ILabelService, LabelService } from 'vs/platform/label/common/label';
import { IDownloadService } from 'vs/platform/download/common/download';
import { DownloadService } from 'vs/platform/download/node/downloadService';
import { DownloadServiceChannel } from 'vs/platform/download/node/downloadIpc';
import { TextResourcePropertiesService } from 'vs/workbench/services/textfile/electron-browser/textResourcePropertiesService';
import { MulitExtensionManagementService } from 'vs/platform/extensionManagement/node/multiExtensionManagement';
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-browser/remoteAuthorityResolverService';
// {{SQL CARBON EDIT}}
import { FileTelemetryService } from 'sql/workbench/services/telemetry/node/fileTelemetryService';
@@ -110,8 +117,7 @@ export interface ICoreServices {
configurationService: IConfigurationService;
environmentService: IEnvironmentService;
logService: ILogService;
timerService: ITimerService;
storageService: IStorageService;
storageService: DelegatingStorageService;
}
/**
@@ -119,15 +125,17 @@ export interface ICoreServices {
* With the Shell being the top level element in the page, it is also responsible for driving the layouting.
*/
export class WorkbenchShell extends Disposable {
private storageService: IStorageService;
private readonly _onWillShutdown = this._register(new Emitter<WillShutdownEvent>());
get onWillShutdown(): Event<WillShutdownEvent> { return this._onWillShutdown.event; }
private storageService: DelegatingStorageService;
private environmentService: IEnvironmentService;
private logService: ILogService;
private configurationService: IConfigurationService;
private contextService: IWorkspaceContextService;
private telemetryService: ITelemetryService;
private extensionService: ExtensionService;
private broadcastService: IBroadcastService;
private timerService: ITimerService;
private themeService: WorkbenchThemeService;
private lifecycleService: LifecycleService;
private mainProcessServices: ServiceCollection;
@@ -151,7 +159,6 @@ export class WorkbenchShell extends Disposable {
this.configurationService = coreServices.configurationService;
this.environmentService = coreServices.environmentService;
this.logService = coreServices.logService;
this.timerService = coreServices.timerService;
this.storageService = coreServices.storageService;
this.mainProcessServices = mainProcessServices;
@@ -160,12 +167,20 @@ export class WorkbenchShell extends Disposable {
}
private renderContents(): void {
// ARIA
aria.setARIAContainer(document.body);
// Instantiation service with services
const [instantiationService, serviceCollection] = this.initServiceCollection(this.container);
// Warm up font cache information before building up too many dom elements
restoreFontInfo(this.storageService);
readFontInfo(BareFontInfo.createFromRawSettings(this.configurationService.getValue('editor'), browser.getZoomLevel()));
this._register(this.storageService.onWillSaveState(() => {
saveFontInfo(this.storageService); // Keep font info for next startup around
}));
// Workbench
this.workbench = this.createWorkbench(instantiationService, serviceCollection, this.container);
@@ -177,53 +192,42 @@ export class WorkbenchShell extends Disposable {
this.logService.warn('Workbench did not finish loading in 10 seconds, that might be a problem that should be reported.');
}, 10000);
this.lifecycleService.when(LifecyclePhase.Running).then(() => {
this.lifecycleService.when(LifecyclePhase.Restored).then(() => {
clearTimeout(timeoutHandle);
});
}
private createWorkbench(instantiationService: IInstantiationService, serviceCollection: ServiceCollection, container: HTMLElement): Workbench {
function handleStartupError(logService: ILogService, error: Error): void {
// Log it
logService.error(toErrorMessage(error, true));
// Rethrow
throw error;
}
try {
const workbench = instantiationService.createInstance(Workbench, container, this.configuration, serviceCollection, this.lifecycleService, this.mainProcessClient);
// Set lifecycle phase to `Restoring`
this.lifecycleService.phase = LifecyclePhase.Restoring;
// Startup Workbench
workbench.startup().done(startupInfos => {
// Set lifecycle phase to `Runnning` so that other contributions can now do something
this.lifecycleService.phase = LifecyclePhase.Running;
workbench.startup().then(startupInfos => {
// Startup Telemetry
this.logStartupTelemetry(startupInfos);
// Set lifecycle phase to `Runnning For A Bit` after a short delay
let eventuallPhaseTimeoutHandle = setTimeout(() => {
eventuallPhaseTimeoutHandle = void 0;
this.lifecycleService.phase = LifecyclePhase.Eventually;
}, 3000);
this._register(toDisposable(() => {
if (eventuallPhaseTimeoutHandle) {
clearTimeout(eventuallPhaseTimeoutHandle);
}
}));
// localStorage metrics (TODO@Ben remove me later)
if (!this.environmentService.extensionTestsPath && this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) {
this.logLocalStorageMetrics();
// Storage Telemetry (TODO@Ben remove me later, including storage errors)
if (!this.environmentService.extensionTestsPath) {
this.logStorageTelemetry();
}
});
}, error => handleStartupError(this.logService, error));
return workbench;
} catch (error) {
handleStartupError(this.logService, error);
// Log it
this.logService.error(toErrorMessage(error, true));
// Rethrow
throw error;
return void 0;
}
}
@@ -272,9 +276,9 @@ export class WorkbenchShell extends Disposable {
// {{SQL CARBON EDIT}}
private sendUsageEvents(): void {
const dailyLastUseDate = Date.parse(this.storageService.get('telemetry.dailyLastUseDate'));
const weeklyLastUseDate = Date.parse(this.storageService.get('telemetry.weeklyLastUseDate'));
const monthlyLastUseDate = Date.parse(this.storageService.get('telemetry.monthlyLastUseDate'));
const dailyLastUseDate = Date.parse(this.storageService.get('telemetry.dailyLastUseDate', StorageScope.GLOBAL, '0'));
const weeklyLastUseDate = Date.parse(this.storageService.get('telemetry.weeklyLastUseDate', StorageScope.GLOBAL, '0'));
const monthlyLastUseDate = Date.parse(this.storageService.get('telemetry.monthlyLastUseDate', StorageScope.GLOBAL, '0'));
let today = new Date().toUTCString();
@@ -282,68 +286,124 @@ export class WorkbenchShell extends Disposable {
if (this.diffInDays(Date.parse(today), dailyLastUseDate) >= 1) {
// daily first use
this.telemetryService.publicLog('telemetry.dailyFirstUse', { dailyFirstUse: true });
this.storageService.store('telemetry.dailyLastUseDate', today);
this.storageService.store('telemetry.dailyLastUseDate', today, StorageScope.GLOBAL);
}
// weekly user event
if (this.diffInDays(Date.parse(today), weeklyLastUseDate) >= 7) {
// weekly first use
this.telemetryService.publicLog('telemetry.weeklyFirstUse', { weeklyFirstUse: true });
this.storageService.store('telemetry.weeklyLastUseDate', today);
this.storageService.store('telemetry.weeklyLastUseDate', today, StorageScope.GLOBAL);
}
// monthly user events
if (this.diffInDays(Date.parse(today), monthlyLastUseDate) >= 30) {
this.telemetryService.publicLog('telemetry.monthlyUse', { monthlyFirstUse: true });
this.storageService.store('telemetry.monthlyLastUseDate', today);
this.storageService.store('telemetry.monthlyLastUseDate', today, StorageScope.GLOBAL);
}
}
private logLocalStorageMetrics(): void {
if (this.lifecycleService.startupKind === StartupKind.ReloadedWindow || this.lifecycleService.startupKind === StartupKind.ReopenedWindow) {
return; // avoid logging localStorage metrics for reload/reopen, we prefer cold startup numbers
}
private logStorageTelemetry(): void {
const initialStartup = !!this.configuration.isInitialStartup;
perf.mark('willReadLocalStorage');
const readyToSend = this.storageService.getBoolean('localStorageMetricsReadyToSend2');
perf.mark('didReadLocalStorage');
const appReadyDuration = initialStartup ? perf.getDuration('main:started', 'main:appReady') : 0;
const workbenchReadyDuration = perf.getDuration(initialStartup ? 'main:started' : 'main:loadWindow', 'didStartWorkbench');
const workspaceStorageRequireDuration = perf.getDuration('willRequireSQLite', 'didRequireSQLite');
const workspaceStorageSchemaDuration = perf.getDuration('willSetupSQLiteSchema', 'didSetupSQLiteSchema');
const globalStorageInitDurationMain = perf.getDuration('main:willInitGlobalStorage', 'main:didInitGlobalStorage');
const globalStorageInitDuratioRenderer = perf.getDuration('willInitGlobalStorage', 'didInitGlobalStorage');
const workspaceStorageInitDuration = perf.getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage');
const workspaceStorageFileExistsDuration = perf.getDuration('willCheckWorkspaceStorageExists', 'didCheckWorkspaceStorageExists');
const workspaceStorageMigrationDuration = perf.getDuration('willMigrateWorkspaceStorageKeys', 'didMigrateWorkspaceStorageKeys');
const workbenchLoadDuration = perf.getDuration('willLoadWorkbenchMain', 'didLoadWorkbenchMain');
const localStorageDuration = perf.getDuration('willReadLocalStorage', 'didReadLocalStorage');
if (!readyToSend) {
this.storageService.store('localStorageMetricsReadyToSend2', true);
return; // avoid logging localStorage metrics directly after the update, we prefer cold startup numbers
}
// Handle errors (avoid duplicates to reduce spam)
const loggedStorageErrors = new Set<string>();
this._register(this.storageService.storage.onWorkspaceStorageError(error => {
const errorStr = `${error}`;
if (!this.storageService.getBoolean('localStorageMetricsSent2')) {
perf.mark('willWriteLocalStorage');
this.storageService.store('localStorageMetricsSent2', true);
perf.mark('didWriteLocalStorage');
perf.mark('willStatLocalStorage');
stat(join(this.environmentService.userDataPath, 'Local Storage', 'file__0.localstorage'), (error, stat) => {
perf.mark('didStatLocalStorage');
if (!loggedStorageErrors.has(errorStr)) {
loggedStorageErrors.add(errorStr);
/* __GDPR__
"localStorageTimers<NUMBER>" : {
"statTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"accessTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"firstReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"subsequentReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"writeTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"keys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"size": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
"sqliteStorageError<NUMBER>" : {
"appReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workbenchReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workspaceExistsTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workspaceMigrationTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workspaceRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workspaceSchemaTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"globalReadTimeMain" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"globalReadTimeRenderer" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"localStorageTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"storageError": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('localStorageTimers2', {
'statTime': perf.getDuration('willStatLocalStorage', 'didStatLocalStorage'),
'accessTime': perf.getDuration('willAccessLocalStorage', 'didAccessLocalStorage'),
'firstReadTime': perf.getDuration('willReadWorkspaceIdentifier', 'didReadWorkspaceIdentifier'),
'subsequentReadTime': perf.getDuration('willReadLocalStorage', 'didReadLocalStorage'),
'writeTime': perf.getDuration('willWriteLocalStorage', 'didWriteLocalStorage'),
'keys': window.localStorage.length,
'size': stat ? stat.size : -1
this.telemetryService.publicLog('sqliteStorageError5', {
'appReadyTime': appReadyDuration,
'workbenchReadyTime': workbenchReadyDuration,
'workspaceExistsTime': workspaceStorageFileExistsDuration,
'workspaceMigrationTime': workspaceStorageMigrationDuration,
'workspaceRequireTime': workspaceStorageRequireDuration,
'workspaceSchemaTime': workspaceStorageSchemaDuration,
'globalReadTimeMain': globalStorageInitDurationMain,
'globalReadTimeRenderer': globalStorageInitDuratioRenderer,
'workspaceReadTime': workspaceStorageInitDuration,
'localStorageTime': localStorageDuration,
'workbenchRequireTime': workbenchLoadDuration,
'workspaceKeys': this.storageService.storage.getSize(StorageScope.WORKSPACE),
'startupKind': this.lifecycleService.startupKind,
'storageError': errorStr
});
});
}
}));
if (this.storageService.storage.hasErrors) {
return; // do not log performance numbers when errors occured
}
if (this.environmentService.verbose) {
return; // do not log when running in verbose mode
}
/* __GDPR__
"sqliteStorageTimers<NUMBER>" : {
"appReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workbenchReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workspaceExistsTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workspaceMigrationTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workspaceRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workspaceSchemaTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"globalReadTimeMain" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"globalReadTimeRenderer" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"localStorageTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('sqliteStorageTimers5', {
'appReadyTime': appReadyDuration,
'workbenchReadyTime': workbenchReadyDuration,
'workspaceExistsTime': workspaceStorageFileExistsDuration,
'workspaceMigrationTime': workspaceStorageMigrationDuration,
'workspaceRequireTime': workspaceStorageRequireDuration,
'workspaceSchemaTime': workspaceStorageSchemaDuration,
'globalReadTimeMain': globalStorageInitDurationMain,
'globalReadTimeRenderer': globalStorageInitDuratioRenderer,
'workspaceReadTime': workspaceStorageInitDuration,
'localStorageTime': localStorageDuration,
'workbenchRequireTime': workbenchLoadDuration,
'workspaceKeys': this.storageService.storage.getSize(StorageScope.WORKSPACE),
'startupKind': this.lifecycleService.startupKind
});
}
private initServiceCollection(container: HTMLElement): [IInstantiationService, ServiceCollection] {
@@ -351,10 +411,10 @@ export class WorkbenchShell extends Disposable {
serviceCollection.set(IWorkspaceContextService, this.contextService);
serviceCollection.set(IConfigurationService, this.configurationService);
serviceCollection.set(IEnvironmentService, this.environmentService);
serviceCollection.set(ILabelService, new SyncDescriptor(LabelService));
serviceCollection.set(ILogService, this._register(this.logService));
serviceCollection.set(ITimerService, this.timerService);
serviceCollection.set(IStorageService, this.storageService);
this.mainProcessServices.forEach((serviceIdentifier, serviceInstance) => {
serviceCollection.set(serviceIdentifier, serviceInstance);
});
@@ -367,17 +427,14 @@ export class WorkbenchShell extends Disposable {
this.broadcastService = instantiationService.createInstance(BroadcastService, this.configuration.windowId);
serviceCollection.set(IBroadcastService, this.broadcastService);
serviceCollection.set(IWindowService, new SyncDescriptor(WindowService, this.configuration.windowId, this.configuration));
serviceCollection.set(IWindowService, new SyncDescriptor(WindowService, [this.configuration.windowId, this.configuration]));
const sharedProcess = (<IWindowsService>serviceCollection.get(IWindowsService)).whenSharedProcessReady()
.then(() => connectNet(this.environmentService.sharedIPCHandle, `window:${this.configuration.windowId}`));
sharedProcess
.done(client => client.registerChannel('dialog', instantiationService.createInstance(DialogChannel)));
// Warm up font cache information before building up too many dom elements
restoreFontInfo(this.storageService);
readFontInfo(BareFontInfo.createFromRawSettings(this.configurationService.getValue('editor'), browser.getZoomLevel()));
sharedProcess.then(client => {
client.registerChannel('dialog', instantiationService.createInstance(DialogChannel));
});
// Hash
serviceCollection.set(IHashService, new SyncDescriptor(HashService));
@@ -386,9 +443,9 @@ export class WorkbenchShell extends Disposable {
if (this.environmentService.args['perf-test']) {
let telemetryOutput = this.environmentService.args['telemetry-output'];
this.telemetryService = new FileTelemetryService(telemetryOutput);
// Telemetry
} else if (this.environmentService.isBuilt && !this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
const channel = getDelayedChannel<ITelemetryAppenderChannel>(sharedProcess.then(c => c.getChannel('telemetryAppender')));
// Telemetry
} else if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender')));
const config: ITelemetryServiceConfig = {
appender: combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService)),
commonProperties: resolveWorkbenchCommonProperties(this.storageService, product.commit, pkg.version, this.configuration.machineId, this.environmentService.installSourcePath),
@@ -416,27 +473,37 @@ export class WorkbenchShell extends Disposable {
serviceCollection.set(IDialogService, instantiationService.createInstance(DialogService));
const lifecycleService = instantiationService.createInstance(LifecycleService);
this._register(lifecycleService.onShutdown(reason => this.dispose(reason)));
this._register(lifecycleService.onWillShutdown(event => this._onWillShutdown.fire(event)));
this._register(lifecycleService.onShutdown(() => this.dispose()));
serviceCollection.set(ILifecycleService, lifecycleService);
this.lifecycleService = lifecycleService;
const extensionManagementChannel = getDelayedChannel<IExtensionManagementChannel>(sharedProcess.then(c => c.getChannel('extensions')));
const extensionManagementChannelClient = new ExtensionManagementChannelClient(extensionManagementChannel, DefaultURITransformer);
serviceCollection.set(IExtensionManagementServerService, new SyncDescriptor(ExtensionManagementServerService, extensionManagementChannelClient));
serviceCollection.set(IRequestService, new SyncDescriptor(RequestService));
serviceCollection.set(IDownloadService, new SyncDescriptor(DownloadService));
serviceCollection.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
const remoteAuthorityResolverService = new RemoteAuthorityResolverService();
serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService);
const remoteAgentService = new RemoteAgentService(this.configuration, this.notificationService, this.environmentService, remoteAuthorityResolverService);
serviceCollection.set(IRemoteAgentService, remoteAgentService);
const remoteAgentConnection = remoteAgentService.getConnection();
if (remoteAgentConnection) {
remoteAgentConnection.registerChannel('dialog', instantiationService.createInstance(DialogChannel));
remoteAgentConnection.registerChannel('download', new DownloadServiceChannel());
remoteAgentConnection.registerChannel('loglevel', new LogLevelSetterChannel(this.logService));
}
const extensionManagementChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('extensions')));
const extensionManagementChannelClient = new ExtensionManagementChannelClient(extensionManagementChannel);
serviceCollection.set(IExtensionManagementServerService, new SyncDescriptor(ExtensionManagementServerService, [extensionManagementChannelClient]));
serviceCollection.set(IExtensionManagementService, new SyncDescriptor(MulitExtensionManagementService));
const extensionEnablementService = this._register(instantiationService.createInstance(ExtensionEnablementService));
serviceCollection.set(IExtensionEnablementService, extensionEnablementService);
serviceCollection.set(IRequestService, new SyncDescriptor(RequestService));
this.extensionService = instantiationService.createInstance(ExtensionService);
serviceCollection.set(IExtensionService, this.extensionService);
perf.mark('willLoadExtensions');
this.extensionService.whenInstalledExtensionsRegistered().done(() => {
perf.mark('didLoadExtensions');
});
serviceCollection.set(IExtensionService, instantiationService.createInstance(ExtensionService));
this.themeService = instantiationService.createInstance(WorkbenchThemeService, document.body);
serviceCollection.set(IWorkbenchThemeService, this.themeService);
@@ -447,10 +514,12 @@ export class WorkbenchShell extends Disposable {
serviceCollection.set(IModeService, new SyncDescriptor(WorkbenchModeServiceImpl));
serviceCollection.set(IModelService, new SyncDescriptor(ModelServiceImpl));
serviceCollection.set(ITextResourceConfigurationService, new SyncDescriptor(TextResourceConfigurationService));
serviceCollection.set(ITextResourcePropertiesService, new SyncDescriptor(TextResourcePropertiesService));
serviceCollection.set(IModelService, new SyncDescriptor(ModelServiceImpl));
serviceCollection.set(IEditorWorkerService, new SyncDescriptor(EditorWorkerServiceImpl));
serviceCollection.set(IUntitledEditorService, new SyncDescriptor(UntitledEditorService));
@@ -469,8 +538,8 @@ export class WorkbenchShell extends Disposable {
serviceCollection.set(IIntegrityService, new SyncDescriptor(IntegrityServiceImpl));
const localizationsChannel = getDelayedChannel<ILocalizationsChannel>(sharedProcess.then(c => c.getChannel('localizations')));
serviceCollection.set(ILocalizationsService, new SyncDescriptor(LocalizationsChannelClient, localizationsChannel));
const localizationsChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('localizations')));
serviceCollection.set(ILocalizationsService, new SyncDescriptor(LocalizationsChannelClient, [localizationsChannel]));
return [instantiationService, serviceCollection];
}
@@ -478,9 +547,10 @@ export class WorkbenchShell extends Disposable {
open(): void {
// Listen on unhandled rejection events
window.addEventListener('unhandledrejection', (event) => {
window.addEventListener('unhandledrejection', (event: PromiseRejectionEvent) => {
// See https://developer.mozilla.org/en-US/docs/Web/API/PromiseRejectionEvent
errors.onUnexpectedError((<any>event).reason);
errors.onUnexpectedError(event.reason);
// Prevent the printing of this event to the console
event.preventDefault();
@@ -502,16 +572,31 @@ export class WorkbenchShell extends Disposable {
// Listeners
this.registerListeners();
// Set lifecycle phase to `Ready`
this.lifecycleService.phase = LifecyclePhase.Ready;
}
private registerListeners(): void {
this._register(addDisposableListener(window, EventType.RESIZE, e => this.onWindowResize(e, true)));
}
// Resize
this._register(addDisposableListener(window, EventType.RESIZE, e => {
if (e.target === window) {
this.layout();
private onWindowResize(e: any, retry: boolean): void {
if (e.target === window) {
if (window.document && window.document.body && window.document.body.clientWidth === 0) {
// TODO@Ben this is an electron issue on macOS when simple fullscreen is enabled
// where for some reason the window clientWidth is reported as 0 when switching
// between simple fullscreen and normal screen. In that case we schedule the layout
// call at the next animation frame once, in the hope that the dimensions are
// proper then.
if (retry) {
scheduleAtNextAnimationFrame(() => this.onWindowResize(e, false));
}
return;
}
}));
this.layout();
}
}
// {{SQL CARBON EDIT}}
@@ -546,20 +631,18 @@ export class WorkbenchShell extends Disposable {
this.workbench.layout();
}
dispose(reason = ShutdownReason.QUIT): void {
dispose(): void {
super.dispose();
// Keep font info for next startup around
saveFontInfo(this.storageService);
// Dispose Workbench
if (this.workbench) {
this.workbench.dispose(reason);
this.workbench.dispose();
}
this.mainProcessClient.dispose();
}
}
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
// Foreground

View File

@@ -3,13 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import * as errors from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import * as arrays from 'vs/base/common/arrays';
import * as objects from 'vs/base/common/objects';
import * as DOM from 'vs/base/browser/dom';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
@@ -19,10 +15,10 @@ import { toResource, IUntitledResourceInput } from 'vs/workbench/common/editor';
import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { IWindowsService, IWindowService, IWindowSettings, IPath, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest } from 'vs/platform/windows/common/windows';
import { IWindowsService, IWindowService, IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IPathData } from 'vs/platform/windows/common/windows';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
import { IWorkbenchThemeService, VS_HC_THEME, VS_DARK_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IWorkbenchThemeService, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService';
import * as browser from 'vs/base/browser/browser';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IResourceInput } from 'vs/platform/editor/common/editor';
@@ -42,18 +38,16 @@ import { AccessibilitySupport, isRootUser, isWindows, isMacintosh } from 'vs/bas
import product from 'vs/platform/node/product';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
const TextInputActions: IAction[] = [
new Action('undo', nls.localize('undo', "Undo"), null, true, () => document.execCommand('undo') && TPromise.as(true)),
new Action('redo', nls.localize('redo', "Redo"), null, true, () => document.execCommand('redo') && TPromise.as(true)),
new Action('undo', nls.localize('undo', "Undo"), null, true, () => document.execCommand('undo') && Promise.resolve(true)),
new Action('redo', nls.localize('redo', "Redo"), null, true, () => document.execCommand('redo') && Promise.resolve(true)),
new Separator(),
new Action('editor.action.clipboardCutAction', nls.localize('cut', "Cut"), null, true, () => document.execCommand('cut') && TPromise.as(true)),
new Action('editor.action.clipboardCopyAction', nls.localize('copy', "Copy"), null, true, () => document.execCommand('copy') && TPromise.as(true)),
new Action('editor.action.clipboardPasteAction', nls.localize('paste', "Paste"), null, true, () => document.execCommand('paste') && TPromise.as(true)),
new Action('editor.action.clipboardCutAction', nls.localize('cut', "Cut"), null, true, () => document.execCommand('cut') && Promise.resolve(true)),
new Action('editor.action.clipboardCopyAction', nls.localize('copy', "Copy"), null, true, () => document.execCommand('copy') && Promise.resolve(true)),
new Action('editor.action.clipboardPasteAction', nls.localize('paste', "Paste"), null, true, () => document.execCommand('paste') && Promise.resolve(true)),
new Separator(),
new Action('editor.action.selectAll', nls.localize('selectAll', "Select All"), null, true, () => document.execCommand('selectAll') && TPromise.as(true))
new Action('editor.action.selectAll', nls.localize('selectAll', "Select All"), null, true, () => document.execCommand('selectAll') && Promise.resolve(true))
];
export class ElectronWindow extends Themable {
@@ -77,9 +71,7 @@ export class ElectronWindow extends Themable {
@IWorkbenchThemeService protected themeService: IWorkbenchThemeService,
@INotificationService private notificationService: INotificationService,
@ICommandService private commandService: ICommandService,
@IExtensionService private extensionService: IExtensionService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IKeybindingService private keybindingService: IKeybindingService,
@ITelemetryService private telemetryService: ITelemetryService,
@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService,
@IFileService private fileService: IFileService,
@@ -112,7 +104,7 @@ export class ElectronWindow extends Themable {
// Support runAction event
ipc.on('vscode:runAction', (event: any, request: IRunActionInWindowRequest) => {
const args: any[] = [];
const args: any[] = request.args || [];
// If we run an action from the touchbar, we fill in the currently active resource
// as payload because the touch bar items are context aware depending on the editor
@@ -128,7 +120,7 @@ export class ElectronWindow extends Themable {
args.push({ from: request.from }); // TODO@telemetry this is a bit weird to send this to every action?
}
this.commandService.executeCommand(request.id, ...args).done(_ => {
this.commandService.executeCommand(request.id, ...args).then(_ => {
/* __GDPR__
"commandExecuted" : {
"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
@@ -141,22 +133,7 @@ export class ElectronWindow extends Themable {
});
});
// Support resolve keybindings event
ipc.on('vscode:resolveKeybindings', (event: any, rawActionIds: string) => {
let actionIds: string[] = [];
try {
actionIds = JSON.parse(rawActionIds);
} catch (error) {
// should not happen
}
// Resolve keys using the keybinding service and send back to browser process
this.resolveKeybindings(actionIds).done(keybindings => {
if (keybindings.length) {
ipc.send('vscode:keybindingsResolved', JSON.stringify(keybindings));
}
}, () => errors.onUnexpectedError);
});
// Error reporting from main
ipc.on('vscode:reportError', (event: any, error: string) => {
if (error) {
const errorParsed = JSON.parse(error);
@@ -178,13 +155,13 @@ export class ElectronWindow extends Themable {
// Fullscreen Events
ipc.on('vscode:enterFullScreen', () => {
this.lifecycleService.when(LifecyclePhase.Running).then(() => {
this.lifecycleService.when(LifecyclePhase.Ready).then(() => {
browser.setFullscreen(true);
});
});
ipc.on('vscode:leaveFullScreen', () => {
this.lifecycleService.when(LifecyclePhase.Running).then(() => {
this.lifecycleService.when(LifecyclePhase.Ready).then(() => {
browser.setFullscreen(false);
});
});
@@ -193,7 +170,7 @@ export class ElectronWindow extends Themable {
ipc.on('vscode:enterHighContrast', () => {
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
if (windowConfig && windowConfig.autoDetectHighContrast) {
this.lifecycleService.when(LifecyclePhase.Running).then(() => {
this.lifecycleService.when(LifecyclePhase.Ready).then(() => {
this.themeService.setColorTheme(VS_HC_THEME, null);
});
}
@@ -202,8 +179,8 @@ export class ElectronWindow extends Themable {
ipc.on('vscode:leaveHighContrast', () => {
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
if (windowConfig && windowConfig.autoDetectHighContrast) {
this.lifecycleService.when(LifecyclePhase.Running).then(() => {
this.themeService.setColorTheme(VS_DARK_THEME, null);
this.lifecycleService.when(LifecyclePhase.Ready).then(() => {
this.themeService.restoreColorTheme();
});
}
});
@@ -230,7 +207,7 @@ export class ElectronWindow extends Themable {
window.document.addEventListener('contextmenu', e => this.onContextMenu(e));
}
private onContextMenu(e: PointerEvent): void {
private onContextMenu(e: MouseEvent): void {
if (e.target instanceof HTMLElement) {
const target = <HTMLElement>e.target;
if (target.nodeName && (target.nodeName.toLowerCase() === 'input' || target.nodeName.toLowerCase() === 'textarea')) {
@@ -238,7 +215,7 @@ export class ElectronWindow extends Themable {
this.contextMenuService.showContextMenu({
getAnchor: () => e,
getActions: () => TPromise.as(TextInputActions),
getActions: () => TextInputActions,
onHide: () => target.focus() // fixes https://github.com/Microsoft/vscode/issues/52948
});
}
@@ -280,16 +257,16 @@ export class ElectronWindow extends Themable {
return null;
};
// Emit event when vscode has loaded
this.lifecycleService.when(LifecyclePhase.Running).then(() => {
ipc.send('vscode:workbenchLoaded', this.windowService.getCurrentWindowId());
// Emit event when vscode is ready
this.lifecycleService.when(LifecyclePhase.Ready).then(() => {
ipc.send('vscode:workbenchReady', this.windowService.getCurrentWindowId());
});
// Integrity warning
this.integrityService.isPure().then(res => this.titleService.updateProperties({ isPure: res.isPure }));
// Root warning
this.lifecycleService.when(LifecyclePhase.Running).then(() => {
this.lifecycleService.when(LifecyclePhase.Restored).then(() => {
let isAdminPromise: Promise<boolean>;
if (isWindows) {
isAdminPromise = import('native-is-elevated').then(isElevated => isElevated());
@@ -375,28 +352,6 @@ export class ElectronWindow extends Themable {
}
}
private resolveKeybindings(actionIds: string[]): TPromise<{ id: string; label: string, isNative: boolean; }[]> {
return TPromise.join([this.lifecycleService.when(LifecyclePhase.Running), this.extensionService.whenInstalledExtensionsRegistered()]).then(() => {
return arrays.coalesce(actionIds.map(id => {
const binding = this.keybindingService.lookupKeybinding(id);
if (!binding) {
return null;
}
// first try to resolve a native accelerator
const electronAccelerator = binding.getElectronAccelerator();
if (electronAccelerator) {
return { id, label: electronAccelerator, isNative: true };
}
// we need this fallback to support keybindings that cannot show in electron menus (e.g. chords)
const acceleratorLabel = binding.getLabel();
if (acceleratorLabel) {
return { id, label: acceleratorLabel, isNative: false };
}
return null;
}));
});
}
private onAddFoldersRequest(request: IAddFoldersRequest): void {
// Buffer all pending requests
@@ -417,12 +372,12 @@ export class ElectronWindow extends Themable {
this.pendingFoldersToAdd = [];
this.workspaceEditingService.addFolders(foldersToAdd).done(null, errors.onUnexpectedError);
this.workspaceEditingService.addFolders(foldersToAdd);
}
private onOpenFiles(request: IOpenFileRequest): void {
const inputs: IResourceEditor[] = [];
const diffMode = (request.filesToDiff.length === 2);
const diffMode = request.filesToDiff && (request.filesToDiff.length === 2);
if (!diffMode && request.filesToOpen) {
inputs.push(...this.toInputs(request.filesToOpen, false));
@@ -437,26 +392,26 @@ export class ElectronWindow extends Themable {
}
if (inputs.length) {
this.openResources(inputs, diffMode).then(null, errors.onUnexpectedError);
this.openResources(inputs, diffMode);
}
if (request.filesToWait && inputs.length) {
// In wait mode, listen to changes to the editors and wait until the files
// are closed that the user wants to wait for. When this happens we delete
// the wait marker file to signal to the outside that editing is done.
const resourcesToWaitFor = request.filesToWait.paths.map(p => URI.file(p.filePath));
const resourcesToWaitFor = request.filesToWait.paths.map(p => URI.revive(p.fileUri));
const waitMarkerFile = URI.file(request.filesToWait.waitMarkerFilePath);
const unbind = this.editorService.onDidCloseEditor(() => {
if (resourcesToWaitFor.every(resource => !this.editorService.isOpen({ resource }))) {
unbind.dispose();
this.fileService.del(waitMarkerFile).done(null, errors.onUnexpectedError);
this.fileService.del(waitMarkerFile);
}
});
}
}
private openResources(resources: (IResourceInput | IUntitledResourceInput)[], diffMode: boolean): Thenable<any> {
return this.lifecycleService.when(LifecyclePhase.Running).then((): TPromise<any> => {
return this.lifecycleService.when(LifecyclePhase.Ready).then(() => {
// In diffMode we open 2 resources as diff
if (diffMode && resources.length === 2) {
@@ -473,9 +428,9 @@ export class ElectronWindow extends Themable {
});
}
private toInputs(paths: IPath[], isNew: boolean): IResourceEditor[] {
private toInputs(paths: IPathData[], isNew: boolean): IResourceEditor[] {
return paths.map(p => {
const resource = URI.file(p.filePath);
const resource = URI.revive(p.fileUri);
let input: IResourceInput | IUntitledResourceInput;
if (isNew) {
input = { filePath: resource.fsPath, options: { pinned: true } } as IUntitledResourceInput;

File diff suppressed because it is too large Load Diff