diff --git a/README.md b/README.md index 91566a493db..5d5ee55c63a 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ a code editor with what developers need for their core edit-build-debug cycle. Code provides comprehensive editing and debugging support, an extensibility model, and lightweight integration with existing tools. +VS Code is updated monthly with new features and bug fixes. You can download it for Windows, Mac and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases everyday, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated at least daily. +
Hello
', '\n\n\nHello
\n\n\n\n', options); assertFormat('|Hello
|', '\nHello
\n', options); - assertFormat('', '\n\n\n \n\n\n\n', options); + assertFormat('', '\n\n\n \n\n\n\n', options); }); }); diff --git a/extensions/html/server/src/utils/edits.ts b/extensions/html/server/src/utils/edits.ts new file mode 100644 index 00000000000..5983d9014fb --- /dev/null +++ b/extensions/html/server/src/utils/edits.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TextDocument, TextEdit, Position } from 'vscode-languageserver-types'; + +export function applyEdits(document: TextDocument, edits: TextEdit[]): string { + let text = document.getText(); + let sortedEdits = edits.sort((a, b) => { + let startDiff = comparePositions(a.range.start, b.range.start); + if (startDiff === 0) { + return comparePositions(a.range.end, b.range.end); + } + return startDiff; + }); + let lastOffset = text.length; + sortedEdits.forEach(e => { + let startOffset = document.offsetAt(e.range.start); + let endOffset = document.offsetAt(e.range.end); + text = text.substring(0, startOffset) + e.newText + text.substring(endOffset, text.length); + lastOffset = startOffset; + }); + return text; +} + +function comparePositions(p1: Position, p2: Position) { + let diff = p2.line - p1.line; + if (diff === 0) { + return p2.character - p1.character; + } + return diff; +} \ No newline at end of file diff --git a/extensions/markdown/syntaxes/markdown.tmLanguage b/extensions/markdown/syntaxes/markdown.tmLanguage index b0982a11087..238afa870ec 100644 --- a/extensions/markdown/syntaxes/markdown.tmLanguage +++ b/extensions/markdown/syntaxes/markdown.tmLanguage @@ -48,10 +48,10 @@;
constructor(init: (complete: TValueCallback ) => void = (c, e, p) => { }, oncancel?: any) {
- super((c, e, p) => { this.completeCallback = c; this.errorCallback = e; this.progressCallback = p; }, oncancel ? oncancel : () => this.oncancel);
+ let captured: any;
+ super((c, e, p) => {
+ captured = { c, e, p };
+ }, oncancel ? oncancel : () => this.oncancel);
+ this.completeCallback = captured.c;
+ this.errorCallback = captured.e;
+ this.progressCallback = captured.p;
}
private oncancel(): void {
diff --git a/src/vs/code/electron-main/launch.ts b/src/vs/code/electron-main/launch.ts
index 554207d24aa..e413e7775bf 100644
--- a/src/vs/code/electron-main/launch.ts
+++ b/src/vs/code/electron-main/launch.ts
@@ -5,7 +5,7 @@
'use strict';
-import { IWindowsMainService } from 'vs/code/electron-main/windows';
+import { IWindowsMainService, OpenContext } from 'vs/code/electron-main/windows';
import { VSCodeWindow } from 'vs/code/electron-main/window';
import { TPromise } from 'vs/base/common/winjs.base';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
@@ -82,6 +82,7 @@ export class LaunchService implements ILaunchService {
const openUrlArg = args['open-url'] || [];
const openUrl = typeof openUrlArg === 'string' ? [openUrlArg] : openUrlArg;
+ const context = !!userEnv['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.OTHER;
if (openUrl.length > 0) {
openUrl.forEach(url => this.urlService.open(url));
@@ -91,17 +92,19 @@ export class LaunchService implements ILaunchService {
// Otherwise handle in windows service
let usedWindows: VSCodeWindow[];
if (!!args.extensionDevelopmentPath) {
- this.windowsService.openExtensionDevelopmentHostWindow({ cli: args, userEnv });
- } else if (args._.length === 0 && args['new-window']) {
- usedWindows = this.windowsService.open({ cli: args, userEnv, forceNewWindow: true, forceEmpty: true });
+ this.windowsService.openExtensionDevelopmentHostWindow({ context, cli: args, userEnv });
+ } else if (args._.length === 0 && args['new-window'] || args['new-window-if-not-first']) {
+ usedWindows = this.windowsService.open({ context, cli: args, userEnv, forceNewWindow: true, forceEmpty: true });
} else if (args._.length === 0) {
- usedWindows = [this.windowsService.focusLastActive(args)];
+ usedWindows = [this.windowsService.focusLastActive(args, context)];
} else {
usedWindows = this.windowsService.open({
+ context,
cli: args,
userEnv,
- forceNewWindow: args.wait || args['new-window'],
+ forceNewWindow: args.wait || args['new-window'] || args['new-window-if-not-first'],
preferNewWindow: !args['reuse-window'],
+ forceReuseWindow: args['reuse-window'],
diffMode: args.diff
});
}
diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts
index 0accc78830e..5eb0da6e884 100644
--- a/src/vs/code/electron-main/main.ts
+++ b/src/vs/code/electron-main/main.ts
@@ -11,7 +11,7 @@ import * as platform from 'vs/base/common/platform';
import { parseMainProcessArgv } from 'vs/platform/environment/node/argv';
import { mkdirp } from 'vs/base/node/pfs';
import { validatePaths } from 'vs/code/electron-main/paths';
-import { IWindowsMainService, WindowsManager } from 'vs/code/electron-main/windows';
+import { IWindowsMainService, WindowsManager, OpenContext } from 'vs/code/electron-main/windows';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc';
import { WindowsService } from 'vs/platform/windows/electron-main/windowsService';
@@ -251,12 +251,13 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo
windowsMainService.ready(userEnv);
// Open our first window
+ const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.OTHER;
if (environmentService.args['new-window'] && environmentService.args._.length === 0) {
- windowsMainService.open({ cli: environmentService.args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths
+ windowsMainService.open({ context, cli: environmentService.args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths
} else if (global.macOpenFiles && global.macOpenFiles.length && (!environmentService.args._ || !environmentService.args._.length)) {
- windowsMainService.open({ cli: environmentService.args, pathsToOpen: global.macOpenFiles, initialStartup: true }); // mac: open-file event received on startup
+ windowsMainService.open({ context: OpenContext.DOCK, cli: environmentService.args, pathsToOpen: global.macOpenFiles, initialStartup: true }); // mac: open-file event received on startup
} else {
- windowsMainService.open({ cli: environmentService.args, forceNewWindow: environmentService.args['new-window'], diffMode: environmentService.args.diff, initialStartup: true }); // default: read paths from cli
+ windowsMainService.open({ context, cli: environmentService.args, forceNewWindow: environmentService.args['new-window'] || environmentService.args['new-window-if-not-first'], diffMode: environmentService.args.diff, initialStartup: true }); // default: read paths from cli
}
// Install Menu
diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts
index 9a89145c64f..7bfc3bc7ec3 100644
--- a/src/vs/code/electron-main/menus.ts
+++ b/src/vs/code/electron-main/menus.ts
@@ -10,7 +10,7 @@ import * as platform from 'vs/base/common/platform';
import * as arrays from 'vs/base/common/arrays';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ipcMain as ipc, app, shell, dialog, Menu, MenuItem } from 'electron';
-import { IWindowsMainService } from 'vs/code/electron-main/windows';
+import { IWindowsMainService, OpenContext } from 'vs/code/electron-main/windows';
import { VSCodeWindow } from 'vs/code/electron-main/window';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IStorageService } from 'vs/code/electron-main/storage';
@@ -315,7 +315,7 @@ export class VSCodeMenu {
this.appMenuInstalled = true;
const dockMenu = new Menu();
- dockMenu.append(new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow() }));
+ dockMenu.append(new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow(OpenContext.DOCK) }));
app.dock.setMenu(dockMenu);
}
@@ -351,19 +351,19 @@ export class VSCodeMenu {
let newFile: Electron.MenuItem;
if (hasNoWindows) {
- newFile = new MenuItem(this.likeAction('workbench.action.files.newUntitledFile', { label: mnemonicLabel(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File")), click: () => this.windowsService.openNewWindow() }));
+ newFile = new MenuItem(this.likeAction('workbench.action.files.newUntitledFile', { label: mnemonicLabel(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File")), click: () => this.windowsService.openNewWindow(OpenContext.MENU) }));
} else {
newFile = this.createMenuItem(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File"), 'workbench.action.files.newUntitledFile');
}
- const open = new MenuItem(this.likeAction('workbench.action.files.openFileFolder', { label: mnemonicLabel(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")), click: () => this.windowsService.openFileFolderPicker() }));
- const openFolder = new MenuItem(this.likeAction('workbench.action.files.openFolder', { label: mnemonicLabel(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")), click: () => this.windowsService.openFolderPicker() }));
+ const open = new MenuItem(this.likeAction('workbench.action.files.openFileFolder', { label: mnemonicLabel(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")), click: (menuItem, win, event) => this.windowsService.openFileFolderPicker(this.isOptionClick(event)) }));
+ const openFolder = new MenuItem(this.likeAction('workbench.action.files.openFolder', { label: mnemonicLabel(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")), click: (menuItem, win, event) => this.windowsService.openFolderPicker(this.isOptionClick(event)) }));
let openFile: Electron.MenuItem;
if (hasNoWindows) {
- openFile = new MenuItem(this.likeAction('workbench.action.files.openFile', { label: mnemonicLabel(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")), click: () => this.windowsService.openFilePicker() }));
+ openFile = new MenuItem(this.likeAction('workbench.action.files.openFile', { label: mnemonicLabel(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")), click: (menuItem, win, event) => this.windowsService.openFilePicker(this.isOptionClick(event)) }));
} else {
- openFile = this.createMenuItem(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File..."), 'workbench.action.files.openFile');
+ openFile = this.createMenuItem(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File..."), ['workbench.action.files.openFile', 'workbench.action.files.openFileInNewWindow']);
}
const openRecentMenu = new Menu();
@@ -379,7 +379,7 @@ export class VSCodeMenu {
const preferences = this.getPreferencesMenu();
- const newWindow = new MenuItem(this.likeAction('workbench.action.newWindow', { label: mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow() }));
+ const newWindow = new MenuItem(this.likeAction('workbench.action.newWindow', { label: mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow(OpenContext.MENU) }));
const revertFile = this.createMenuItem(nls.localize({ key: 'miRevert', comment: ['&& denotes a mnemonic'] }, "Re&&vert File"), 'workbench.action.files.revert', this.windowsService.getWindowCount() > 0);
const closeWindow = new MenuItem(this.likeAction('workbench.action.closeWindow', { label: mnemonicLabel(nls.localize({ key: 'miCloseWindow', comment: ['&& denotes a mnemonic'] }, "Clos&&e Window")), click: () => this.windowsService.getLastActiveWindow().win.close(), enabled: this.windowsService.getWindowCount() > 0 }));
@@ -468,10 +468,15 @@ export class VSCodeMenu {
}
private createOpenRecentMenuItem(path: string, actionId: string): Electron.MenuItem {
+ let label = path;
+ if ((platform.isMacintosh || platform.isLinux) && path.indexOf(this.environmentService.userHome) === 0) {
+ label = `~${path.substr(this.environmentService.userHome.length)}`;
+ }
+
return new MenuItem(this.likeAction(actionId, {
- label: unMnemonicLabel(path), click: (menuItem, win, event) => {
- const openInNewWindow = event && ((!platform.isMacintosh && event.ctrlKey) || (platform.isMacintosh && event.metaKey));
- const success = !!this.windowsService.open({ cli: this.environmentService.args, pathsToOpen: [path], forceNewWindow: openInNewWindow });
+ label: unMnemonicLabel(label), click: (menuItem, win, event) => {
+ const openInNewWindow = this.isOptionClick(event);
+ const success = !!this.windowsService.open({ context: OpenContext.MENU, cli: this.environmentService.args, pathsToOpen: [path], forceNewWindow: openInNewWindow });
if (!success) {
this.windowsService.removeFromRecentPathsList(path);
}
@@ -479,6 +484,10 @@ export class VSCodeMenu {
}, false));
}
+ private isOptionClick(event: Electron.Event): boolean {
+ return event && ((!platform.isMacintosh && (event.ctrlKey || event.shiftKey)) || (platform.isMacintosh && (event.metaKey || event.altKey)));
+ }
+
private createRoleMenuItem(label: string, actionId: string, role: Electron.MenuItemRole): Electron.MenuItem {
const options: Electron.MenuItemOptions = {
label: mnemonicLabel(label),
@@ -890,11 +899,18 @@ export class VSCodeMenu {
}
}
- private createMenuItem(label: string, actionId: string, enabled?: boolean, checked?: boolean): Electron.MenuItem;
+ private createMenuItem(label: string, actionId: string | string[], enabled?: boolean, checked?: boolean): Electron.MenuItem;
private createMenuItem(label: string, click: () => void, enabled?: boolean, checked?: boolean): Electron.MenuItem;
private createMenuItem(arg1: string, arg2: any, arg3?: boolean, arg4?: boolean): Electron.MenuItem {
const label = mnemonicLabel(arg1);
- const click: () => void = (typeof arg2 === 'function') ? arg2 : () => this.windowsService.sendToFocused('vscode:runAction', arg2);
+ const click: () => void = (typeof arg2 === 'function') ? arg2 : (menuItem, win, event) => {
+ let actionId = arg2;
+ if (Array.isArray(arg2)) {
+ actionId = this.isOptionClick(event) ? arg2[1] : arg2[0]; // support alternative action if we got multiple action Ids and the option key was pressed while invoking
+ }
+
+ this.windowsService.sendToFocused('vscode:runAction', actionId);
+ };
const enabled = typeof arg3 === 'boolean' ? arg3 : this.windowsService.getWindowCount() > 0;
const checked = typeof arg4 === 'boolean' ? arg4 : false;
diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts
index 5289884c526..dd967a6e14c 100644
--- a/src/vs/code/electron-main/window.ts
+++ b/src/vs/code/electron-main/window.ts
@@ -82,6 +82,7 @@ export interface IWindowConfiguration extends ParsedArgs {
isInitialStartup?: boolean;
perfStartTime?: number;
+ perfAppReady?: number;
perfWindowLoadTime?: number;
workspacePath?: string;
@@ -205,21 +206,6 @@ export class VSCodeWindow implements IVSCodeWindow {
this._win = new BrowserWindow(options);
this._id = this._win.id;
- // TODO@joao: hook this up to some initialization routine
- // this causes a race between setting the headers and doing
- // a request that needs them. chances are low
- getCommonHTTPHeaders().done(headers => {
- if (!this._win) {
- return;
- }
-
- const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*'];
-
- this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => {
- cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) });
- });
- });
-
if (isFullscreenOrMaximized) {
this.win.maximize();
@@ -238,9 +224,28 @@ export class VSCodeWindow implements IVSCodeWindow {
this.setMenuBarVisibility(false); // respect configured menu bar visibility
}
+ // TODO@joao: hook this up to some initialization routine
+ // this causes a race between setting the headers and doing
+ // a request that needs them. chances are low
+ this.setCommonHTTPHeaders();
+
this.registerListeners();
}
+ private setCommonHTTPHeaders(): void {
+ getCommonHTTPHeaders().done(headers => {
+ if (!this._win) {
+ return;
+ }
+
+ const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*'];
+
+ this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => {
+ cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) });
+ });
+ });
+ }
+
public hasHiddenTitleBarStyle(): boolean {
return this.hiddenTitleBarStyle;
}
@@ -478,11 +483,12 @@ export class VSCodeWindow implements IVSCodeWindow {
windowConfiguration.fullscreen = this._win.isFullScreen();
// Set Accessibility Config
- windowConfiguration.highContrast = platform.isWindows && systemPreferences.isInvertedColorScheme();
+ windowConfiguration.highContrast = platform.isWindows && systemPreferences.isInvertedColorScheme() && (!windowConfig || windowConfig.autoDetectHighContrast);
windowConfiguration.accessibilitySupport = app.isAccessibilitySupportEnabled();
// Perf Counters
windowConfiguration.perfStartTime = global.perfStartTime;
+ windowConfiguration.perfAppReady = global.perfAppReady;
windowConfiguration.perfWindowLoadTime = Date.now();
// Config (combination of process.argv and window configuration)
diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts
index 3962312291f..7c442c543d9 100644
--- a/src/vs/code/electron-main/windows.ts
+++ b/src/vs/code/electron-main/windows.ts
@@ -34,12 +34,32 @@ enum WindowError {
CRASHED
}
+export enum OpenContext {
+
+ // opening when running from the command line
+ CLI,
+
+ // macOS only: opening from the dock (also when opening files to a running instance from desktop)
+ DOCK,
+
+ // opening from the main application window
+ MENU,
+
+ // opening from a file or folder dialog
+ DIALOG,
+
+ // any other way of opening
+ OTHER
+}
+
export interface IOpenConfiguration {
+ context: OpenContext;
cli: ParsedArgs;
userEnv?: platform.IProcessEnvironment;
pathsToOpen?: string[];
preferNewWindow?: boolean;
forceNewWindow?: boolean;
+ forceReuseWindow?: boolean;
forceEmpty?: boolean;
windowToUse?: VSCodeWindow;
diffMode?: boolean;
@@ -96,10 +116,10 @@ export interface IWindowsMainService {
openFilePicker(forceNewWindow?: boolean, path?: string, window?: VSCodeWindow): void;
openFolderPicker(forceNewWindow?: boolean, window?: VSCodeWindow): void;
openAccessibilityOptions(): void;
- focusLastActive(cli: ParsedArgs): VSCodeWindow;
+ focusLastActive(cli: ParsedArgs, context: OpenContext): VSCodeWindow;
getLastActiveWindow(): VSCodeWindow;
findWindow(workspacePath: string, filePath?: string, extensionDevelopmentPath?: string): VSCodeWindow;
- openNewWindow(): void;
+ openNewWindow(context: OpenContext): void;
sendToFocused(channel: string, ...args: any[]): void;
sendToAll(channel: string, payload: any, windowIdsToIgnore?: number[]): void;
getFocusedWindow(): VSCodeWindow;
@@ -166,7 +186,7 @@ export class WindowsManager implements IWindowsMainService {
// Mac only event: open new window when we get activated
if (!hasVisibleWindows) {
- this.openNewWindow();
+ this.openNewWindow(OpenContext.DOCK);
}
});
@@ -187,7 +207,12 @@ export class WindowsManager implements IWindowsMainService {
// Handle paths delayed in case more are coming!
runningTimeout = setTimeout(() => {
- this.open({ cli: this.environmentService.args, pathsToOpen: macOpenFiles, preferNewWindow: true /* dropping on the dock prefers to open in a new window */ });
+ this.open({
+ context: OpenContext.DOCK /* can also be opening from finder while app is running */,
+ cli: this.environmentService.args,
+ pathsToOpen: macOpenFiles,
+ preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */
+ });
macOpenFiles = [];
runningTimeout = null;
}, 100);
@@ -272,6 +297,8 @@ export class WindowsManager implements IWindowsMainService {
}
public open(openConfig: IOpenConfiguration): VSCodeWindow[] {
+ const windowConfig = this.configurationService.getConfiguration