Refresh master with initial release/0.24 snapshot (#332)

* Initial port of release/0.24 source code

* Fix additional headers

* Fix a typo in launch.json
This commit is contained in:
Karl Burtram
2017-12-15 15:38:57 -08:00
committed by GitHub
parent 271b3a0b82
commit 6ad0df0e3e
7118 changed files with 107999 additions and 56466 deletions

View File

@@ -12,23 +12,23 @@ import errors = require('vs/base/common/errors');
import types = require('vs/base/common/types');
import { TPromise } from 'vs/base/common/winjs.base';
import arrays = require('vs/base/common/arrays');
import objects = require('vs/base/common/objects');
import DOM = require('vs/base/browser/dom');
import Severity from 'vs/base/common/severity';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { IAction, Action } from 'vs/base/common/actions';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { AutoSaveConfiguration } from 'vs/platform/files/common/files';
import { AutoSaveConfiguration, IFileService } from 'vs/platform/files/common/files';
import { toResource } from 'vs/workbench/common/editor';
import { IWorkbenchEditorService, IResourceInputType } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IMessageService } from 'vs/platform/message/common/message';
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 } from 'vs/platform/windows/common/windows';
import { IWindowsService, IWindowService, IWindowSettings, IPath, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest } from 'vs/platform/windows/common/windows';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
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 * as browser from 'vs/base/browser/browser';
@@ -41,6 +41,12 @@ import { Themable } from 'vs/workbench/common/theme';
import { ipcRenderer as ipc, webFrame } from 'electron';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction } from 'vs/platform/actions/common/actions';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ConfigurationTarget, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
const TextInputActions: IAction[] = [
new Action('undo', nls.localize('undo', "Undo"), null, true, () => document.execCommand('undo') && TPromise.as(true)),
@@ -57,6 +63,13 @@ export class ElectronWindow extends Themable {
private static AUTO_SAVE_SETTING = 'files.autoSave';
private touchBarUpdater: RunOnceScheduler;
private touchBarMenu: IMenu;
private touchBarDisposables: IDisposable[];
private lastInstalledTouchedBar: ICommandAction[][];
private previousConfiguredZoomLevel: number;
constructor(
shellContainer: HTMLElement,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@@ -68,7 +81,6 @@ export class ElectronWindow extends Themable {
@ITitleService private titleService: ITitleService,
@IWorkbenchThemeService protected themeService: IWorkbenchThemeService,
@IMessageService private messageService: IMessageService,
@IConfigurationEditingService private configurationEditingService: IConfigurationEditingService,
@ICommandService private commandService: ICommandService,
@IExtensionService private extensionService: IExtensionService,
@IViewletService private viewletService: IViewletService,
@@ -77,22 +89,34 @@ export class ElectronWindow extends Themable {
@IEnvironmentService private environmentService: IEnvironmentService,
@ITelemetryService private telemetryService: ITelemetryService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService
@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService,
@IFileService private fileService: IFileService,
@IMenuService private menuService: IMenuService,
@IContextKeyService private contextKeyService: IContextKeyService
) {
super(themeService);
this.touchBarDisposables = [];
this.touchBarUpdater = new RunOnceScheduler(() => this.doSetupTouchbar(), 300);
this.toUnbind.push(this.touchBarUpdater);
this.registerListeners();
this.setup();
this.create();
}
private registerListeners(): void {
// React to editor input changes
this.editorGroupService.onEditorsChanged(() => {
const file = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' });
this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => {
// Represented File Name
const file = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' });
this.titleService.setRepresentedFilename(file ? file.fsPath : '');
});
// Touch Bar
this.updateTouchbarMenu();
}));
// prevent opening a real URL inside the shell
[DOM.EventType.DRAG_OVER, DOM.EventType.DROP].forEach(event => {
@@ -101,21 +125,32 @@ export class ElectronWindow extends Themable {
});
});
// Handle window.open() calls
const $this = this;
(<any>window).open = function (url: string, target: string, features: string, replace: boolean) {
$this.windowsService.openExternal(url);
return null;
};
}
private setup(): void {
// Support runAction event
ipc.on('vscode:runAction', (event, actionId: string) => {
this.commandService.executeCommand(actionId, { from: 'menu' }).done(_ => {
this.telemetryService.publicLog('commandExecuted', { id: actionId, from: 'menu' });
ipc.on('vscode:runAction', (event, request: IRunActionInWindowRequest) => {
const args: any[] = [];
// 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
if (request.from === 'touchbar') {
const activeEditor = this.editorService.getActiveEditor();
if (activeEditor) {
const resource = toResource(activeEditor.input, { supportSideBySide: true });
if (resource) {
args.push(resource);
}
}
} else {
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(_ => {
/* __GDPR__
"commandExecuted" : {
"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('commandExecuted', { id: request.id, from: request.from });
}, err => {
this.messageService.show(Severity.Error, err);
});
@@ -138,11 +173,6 @@ export class ElectronWindow extends Themable {
}, () => errors.onUnexpectedError);
});
// Send over all extension viewlets when extensions are ready
this.extensionService.onReady().then(() => {
ipc.send('vscode:extensionViewlets', JSON.stringify(this.viewletService.getViewlets().filter(v => !!v.extensionId).map(v => { return { id: v.id, label: v.name }; })));
});
ipc.on('vscode:reportError', (event, error) => {
if (error) {
const errorParsed = JSON.parse(error);
@@ -157,11 +187,6 @@ export class ElectronWindow extends Themable {
// Support addFolders event if we have a workspace opened
ipc.on('vscode:addFolders', (event, request: IAddFoldersRequest) => this.onAddFolders(request));
// Emit event when vscode has loaded
this.partService.joinCreation().then(() => {
ipc.send('vscode:workbenchLoaded', this.windowService.getCurrentWindowId());
});
// Message support
ipc.on('vscode:showInfoMessage', (event, message: string) => {
this.messageService.show(Severity.Info, message);
@@ -205,8 +230,8 @@ export class ElectronWindow extends Themable {
});
// keyboard layout changed event
ipc.on('vscode:keyboardLayoutChanged', (event, isISOKeyboard: boolean) => {
KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(isISOKeyboard);
ipc.on('vscode:keyboardLayoutChanged', event => {
KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged();
});
// keyboard layout changed event
@@ -215,47 +240,138 @@ export class ElectronWindow extends Themable {
});
// Configuration changes
let previousConfiguredZoomLevel: number;
this.configurationService.onDidUpdateConfiguration(e => {
const windowConfig: IWindowsConfiguration = this.configurationService.getConfiguration<IWindowsConfiguration>();
let newZoomLevel = 0;
if (windowConfig.window && typeof windowConfig.window.zoomLevel === 'number') {
newZoomLevel = windowConfig.window.zoomLevel;
// Leave early if the configured zoom level did not change (https://github.com/Microsoft/vscode/issues/1536)
if (previousConfiguredZoomLevel === newZoomLevel) {
return;
}
previousConfiguredZoomLevel = newZoomLevel;
}
if (webFrame.getZoomLevel() !== newZoomLevel) {
webFrame.setZoomLevel(newZoomLevel);
browser.setZoomFactor(webFrame.getZoomFactor());
// See https://github.com/Microsoft/vscode/issues/26151
// Cannot be trusted because the webFrame might take some time
// until it really applies the new zoom level
browser.setZoomLevel(webFrame.getZoomLevel(), /*isTrusted*/false);
}
});
this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.onDidUpdateConfiguration(e)));
// Context menu support in input/textarea
window.document.addEventListener('contextmenu', e => {
if (e.target instanceof HTMLElement) {
const target = <HTMLElement>e.target;
if (target.nodeName && (target.nodeName.toLowerCase() === 'input' || target.nodeName.toLowerCase() === 'textarea')) {
e.preventDefault();
e.stopPropagation();
window.document.addEventListener('contextmenu', e => this.onContextMenu(e));
}
this.contextMenuService.showContextMenu({
getAnchor: () => e,
getActions: () => TPromise.as(TextInputActions)
});
}
private onContextMenu(e: PointerEvent): void {
if (e.target instanceof HTMLElement) {
const target = <HTMLElement>e.target;
if (target.nodeName && (target.nodeName.toLowerCase() === 'input' || target.nodeName.toLowerCase() === 'textarea')) {
e.preventDefault();
e.stopPropagation();
this.contextMenuService.showContextMenu({
getAnchor: () => e,
getActions: () => TPromise.as(TextInputActions)
});
}
}
}
private onDidUpdateConfiguration(event: IConfigurationChangeEvent): void {
if (!event.affectsConfiguration('window.zoomLevel')) {
return;
}
const windowConfig: IWindowsConfiguration = this.configurationService.getConfiguration<IWindowsConfiguration>();
let newZoomLevel = 0;
if (windowConfig.window && typeof windowConfig.window.zoomLevel === 'number') {
newZoomLevel = windowConfig.window.zoomLevel;
// Leave early if the configured zoom level did not change (https://github.com/Microsoft/vscode/issues/1536)
if (this.previousConfiguredZoomLevel === newZoomLevel) {
return;
}
this.previousConfiguredZoomLevel = newZoomLevel;
}
if (webFrame.getZoomLevel() !== newZoomLevel) {
webFrame.setZoomLevel(newZoomLevel);
browser.setZoomFactor(webFrame.getZoomFactor());
// See https://github.com/Microsoft/vscode/issues/26151
// Cannot be trusted because the webFrame might take some time
// until it really applies the new zoom level
browser.setZoomLevel(webFrame.getZoomLevel(), /*isTrusted*/false);
}
}
private create(): void {
// Handle window.open() calls
const $this = this;
(<any>window).open = function (url: string, target: string, features: string, replace: boolean): any {
$this.windowsService.openExternal(url);
return null;
};
// Send over all extension viewlets when extensions are ready
this.extensionService.onReady().then(() => {
ipc.send('vscode:extensionViewlets', JSON.stringify(this.viewletService.getViewlets().filter(v => !!v.extensionId).map(v => { return { id: v.id, label: v.name }; })));
});
// Emit event when vscode has loaded
this.partService.joinCreation().then(() => {
ipc.send('vscode:workbenchLoaded', this.windowService.getCurrentWindowId());
});
// Touchbar Support
this.updateTouchbarMenu();
}
private updateTouchbarMenu(): void {
if (!platform.isMacintosh) {
return; // macOS only
}
// Dispose old
this.touchBarDisposables = dispose(this.touchBarDisposables);
// Create new
this.touchBarMenu = this.editorGroupService.invokeWithinEditorContext(accessor => this.menuService.createMenu(MenuId.TouchBarContext, accessor.get(IContextKeyService)));
this.touchBarDisposables.push(this.touchBarMenu);
this.touchBarDisposables.push(this.touchBarMenu.onDidChange(() => {
this.scheduleSetupTouchbar();
}));
this.scheduleSetupTouchbar();
}
private scheduleSetupTouchbar(): void {
this.touchBarUpdater.schedule();
}
private doSetupTouchbar(): void {
const actions: (MenuItemAction | Separator)[] = [];
// Fill actions into groups respecting order
fillInActions(this.touchBarMenu, void 0, actions);
// Convert into command action multi array
const items: ICommandAction[][] = [];
let group: ICommandAction[] = [];
for (let i = 0; i < actions.length; i++) {
const action = actions[i];
// Command
if (action instanceof MenuItemAction) {
group.push(action.item);
}
// Separator
else if (action instanceof Separator) {
if (group.length) {
items.push(group);
}
group = [];
}
}
if (group.length) {
items.push(group);
}
// Only update if the actions have changed
if (!objects.equals(this.lastInstalledTouchedBar, items)) {
this.lastInstalledTouchedBar = items;
this.windowService.updateTouchBar(items);
}
}
private resolveKeybindings(actionIds: string[]): TPromise<{ id: string; label: string, isNative: boolean; }[]> {
@@ -284,33 +400,14 @@ export class ElectronWindow extends Themable {
}
private onAddFolders(request: IAddFoldersRequest): void {
const foldersToAdd = request.foldersToAdd.map(folderToAdd => URI.file(folderToAdd.filePath));
const foldersToAdd = request.foldersToAdd.map(folderToAdd => ({ uri: URI.file(folderToAdd.filePath) }));
// Workspace: just add to workspace config
if (this.contextService.hasMultiFolderWorkspace()) {
this.workspaceEditingService.addRoots(foldersToAdd).done(null, errors.onUnexpectedError);
}
// Single folder or no workspace: create workspace and open
else {
let workspaceFolders: URI[] = [];
// Folder of workspace is the first of multi root workspace, so add it
if (this.contextService.hasFolderWorkspace()) {
workspaceFolders.push(...this.contextService.getWorkspace().roots);
}
// Fill in remaining ones from request
workspaceFolders.push(...request.foldersToAdd.map(folderToAdd => URI.file(folderToAdd.filePath)));
// Create workspace and open (ensure no duplicates)
this.windowService.createAndOpenWorkspace(arrays.distinct(workspaceFolders.map(folder => folder.fsPath), folder => platform.isLinux ? folder : folder.toLowerCase()));
}
this.workspaceEditingService.addFolders(foldersToAdd).done(null, errors.onUnexpectedError);
}
private onOpenFiles(request: IOpenFileRequest): void {
let inputs: IResourceInputType[] = [];
let diffMode = (request.filesToDiff.length === 2);
const inputs: IResourceInputType[] = [];
const diffMode = (request.filesToDiff.length === 2);
if (!diffMode && request.filesToOpen) {
inputs.push(...this.toInputs(request.filesToOpen, false));
@@ -327,12 +424,26 @@ export class ElectronWindow extends Themable {
if (inputs.length) {
this.openResources(inputs, diffMode).done(null, errors.onUnexpectedError);
}
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 waitMarkerFile = URI.file(request.filesToWait.waitMarkerFilePath);
const stacks = this.editorGroupService.getStacksModel();
const unbind = stacks.onEditorClosed(() => {
if (resourcesToWaitFor.every(r => !stacks.isOpen(r))) {
unbind.dispose();
this.fileService.del(waitMarkerFile).done(null, errors.onUnexpectedError);
}
});
}
}
private openResources(resources: (IResourceInput | IUntitledResourceInput)[], diffMode: boolean): TPromise<IEditor | IEditor[]> {
return this.partService.joinCreation().then((): TPromise<IEditor | IEditor[]> => {
// In diffMode we open 2 resources as diff
if (diffMode && resources.length === 2) {
return this.editorService.openEditor({ leftResource: resources[0].resource, rightResource: resources[1].resource, options: { pinned: true } });
@@ -376,7 +487,7 @@ export class ElectronWindow extends Themable {
}
private toggleAutoSave(): void {
const setting = this.configurationService.lookup(ElectronWindow.AUTO_SAVE_SETTING);
const setting = this.configurationService.inspect(ElectronWindow.AUTO_SAVE_SETTING);
let userAutoSaveConfig = setting.user;
if (types.isUndefinedOrNull(userAutoSaveConfig)) {
userAutoSaveConfig = setting.default; // use default if setting not defined
@@ -389,6 +500,12 @@ export class ElectronWindow extends Themable {
newAutoSaveValue = AutoSaveConfiguration.AFTER_DELAY;
}
this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: ElectronWindow.AUTO_SAVE_SETTING, value: newAutoSaveValue });
this.configurationService.updateValue(ElectronWindow.AUTO_SAVE_SETTING, newAutoSaveValue, ConfigurationTarget.USER);
}
public dispose(): void {
this.touchBarDisposables = dispose(this.touchBarDisposables);
super.dispose();
}
}