diff --git a/.travis.yml b/.travis.yml index 39e2d97287dfc..ea7107ef9d8af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,8 +26,8 @@ before_install: - git submodule update --init --recursive - git clone --depth 1 https://github.com/creationix/nvm.git ./.nvm - source ./.nvm/nvm.sh - - nvm install 6.6.0 - - nvm use 6.6.0 + - nvm install 7.4.0 + - nvm use 7.4.0 - npm config set python `which python` - npm install -g gulp - if [ $TRAVIS_OS_NAME == "linux" ]; then diff --git a/OSSREADME.json b/OSSREADME.json index 1b0bad842844e..aa24880b7f7c7 100644 --- a/OSSREADME.json +++ b/OSSREADME.json @@ -68,7 +68,7 @@ }, { "name": "chromium", - "version": "53.0.2785.143", + "version": "56.0.2924.87", "repositoryURL": "http://www.chromium.org/Home", "licenseDetail": [ "BSD License", @@ -104,14 +104,14 @@ }, { "name": "libchromiumcontent", - "version": "53.0.2785.143", + "version": "56.0.2924.87", "license": "MIT", "repositoryURL": "https://github.com/electron/libchromiumcontent", "isProd": true }, { "name": "nodejs", - "version": "6.5.0", + "version": "7.4.0", "repositoryURL": "https://github.com/nodejs/node", "isProd": true }, diff --git a/appveyor.yml b/appveyor.yml index f7b9e1f5ae55d..0f70d510c0f3d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,7 +3,7 @@ environment: VSCODE_BUILD_VERBOSE: true install: - - ps: Install-Product node 6.6.0 x64 + - ps: Install-Product node 7.4.0 x64 - npm install -g npm --silent - npm install -g gulp mocha --silent diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index ef084f4913e7f..1cb496b1d39f5 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -13,9 +13,9 @@ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz" }, "applicationinsights": { - "version": "0.17.0", - "from": "applicationinsights@0.17.0", - "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.17.0.tgz" + "version": "0.17.1", + "from": "applicationinsights@0.17.1", + "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.17.1.tgz" }, "arr-diff": { "version": "2.0.0", diff --git a/package.json b/package.json index 59026697ac9f4..4f0b8ff4bcc21 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.12.0", - "electronVersion": "1.4.6", + "electronVersion": "1.6.5", "distro": "fbef8e6a73f122b5c8b0034e2c1549e00a1815c3", "author": { "name": "Microsoft Corporation" @@ -19,7 +19,7 @@ "precommit": "node build/gulpfile.hygiene.js" }, "dependencies": { - "applicationinsights": "0.17.0", + "applicationinsights": "0.17.1", "chokidar": "bpasero/chokidar#vscode", "emmet": "ramya-rao-a/emmet#vscode", "fast-plist": "0.1.2", diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts index ab5d7d9b83090..e85bb7d346fdf 100644 --- a/src/typings/electron.d.ts +++ b/src/typings/electron.d.ts @@ -1318,6 +1318,18 @@ declare namespace Electron { * Note: This API is available only on Windows. */ setThumbnailToolTip(toolTip: string): boolean; + /** + * Sets the application id, app icon, relaunch command and relaunch display name + * for the given window. appIconIndex should be set to 0 if the app icon + * file only has a single icon. + */ + setAppDetails(options: { + appId?: string; + appIconPath?: string; + appIconIndex?: number; + relaunchCommand?: string; + relaunchDisplayName?: string; + }): void; /** * Same as webContents.showDefinitionForSelection(). * Note: This API is available only on macOS. @@ -1784,6 +1796,11 @@ declare namespace Electron { * Settings of web page’s features. */ webPreferences?: WebPreferences; + /** + * Tab group name, allows opening the window as a native tab on macOS 10.12+. + * Windows with the same tabbing identifier will be grouped together. + */ + tabbingIdentifier?: string; } type BrowserWindowType = BrowserWindowTypeLinux | BrowserWindowTypeMac | BrowserWindowTypeWindows; diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 16bd28627c382..5966bb41b2f92 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -746,6 +746,7 @@ export function isHTMLElement(o: any): o is HTMLElement { export const EventType = { // Mouse CLICK: 'click', + AUXCLICK: 'auxclick', // >= Chrome 56 DBLCLICK: 'dblclick', MOUSE_UP: 'mouseup', MOUSE_DOWN: 'mousedown', diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index f5eaa8eadc7e2..ead6d13a82250 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -488,6 +488,7 @@ export class TreeView extends HeightMap { this.viewListeners.push(DOM.addDisposableListener(this.domNode, 'mousedown', (e) => this.onMouseDown(e))); this.viewListeners.push(DOM.addDisposableListener(this.domNode, 'mouseup', (e) => this.onMouseUp(e))); this.viewListeners.push(DOM.addDisposableListener(this.wrapper, 'click', (e) => this.onClick(e))); + this.viewListeners.push(DOM.addDisposableListener(this.wrapper, 'auxclick', (e) => this.onClick(e))); // >= Chrome 56 this.viewListeners.push(DOM.addDisposableListener(this.domNode, 'contextmenu', (e) => this.onContextMenu(e))); this.viewListeners.push(DOM.addDisposableListener(this.wrapper, Touch.EventType.Tap, (e) => this.onTap(e))); this.viewListeners.push(DOM.addDisposableListener(this.wrapper, Touch.EventType.Change, (e) => this.onTouchChange(e))); diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index cab7c54989682..364fb2e8de66e 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -35,7 +35,6 @@ export interface IWindowCreationOptions { state: IWindowState; extensionDevelopmentPath?: string; isExtensionTestHost?: boolean; - titleBarStyle?: 'native' | 'custom'; } export enum WindowMode { @@ -190,7 +189,8 @@ export class VSCodeWindow { show: !isFullscreenOrMaximized, title: product.nameLong, webPreferences: { - 'backgroundThrottling': false // by default if Code is in the background, intervals and timeouts get throttled + 'backgroundThrottling': false, // by default if Code is in the background, intervals and timeouts get throttled, + disableBlinkFeatures: 'Auxclick' // disable auxclick events (see https://developers.google.com/web/updates/2016/10/auxclick) } }; @@ -198,14 +198,26 @@ export class VSCodeWindow { options.icon = path.join(this.environmentService.appRoot, 'resources/linux/code.png'); // Windows and Mac are better off using the embedded icon(s) } + const windowConfig = this.configurationService.getConfiguration('window'); + + let useNativeTabs = false; + if (windowConfig && windowConfig.nativeTabs) { + options.tabbingIdentifier = product.nameShort; // this opts in to sierra tabs + useNativeTabs = true; + } + let useCustomTitleStyle = false; - if (platform.isMacintosh && (!this.options.titleBarStyle || this.options.titleBarStyle === 'custom')) { + if (platform.isMacintosh && (!windowConfig || !windowConfig.titleBarStyle || windowConfig.titleBarStyle === 'custom')) { const isDev = !this.environmentService.isBuilt || !!config.extensionDevelopmentPath; if (!isDev) { useCustomTitleStyle = true; // not enabled when developing due to https://github.com/electron/electron/issues/3647 } } + if (useNativeTabs) { + useCustomTitleStyle = false; // native tabs on sierra do not work with custom title style + } + if (useCustomTitleStyle) { options.titleBarStyle = 'hidden'; this.hiddenTitleBarStyle = true; @@ -219,6 +231,15 @@ export class VSCodeWindow { this._win.setSheetOffset(22); // offset dialogs by the height of the custom title bar if we have any } + // Set relaunch command + if (platform.isWindows && product.win32AppUserModelId && typeof this._win.setAppDetails === 'function') { + this._win.setAppDetails({ + appId: product.win32AppUserModelId, + relaunchCommand: `"${process.execPath}" -n`, + relaunchDisplayName: product.nameLong + }); + } + if (isFullscreenOrMaximized) { this.win.maximize(); @@ -769,4 +790,4 @@ export class VSCodeWindow { this._win = null; // Important to dereference the window object to allow for GC } -} +} \ No newline at end of file diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 011e2fae5ec4c..110a7467b12a2 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -851,8 +851,7 @@ export class WindowsManager implements IWindowsMainService { vscodeWindow = new VSCodeWindow({ state, extensionDevelopmentPath: configuration.extensionDevelopmentPath, - isExtensionTestHost: !!configuration.extensionTestsPath, - titleBarStyle: windowConfig ? windowConfig.titleBarStyle : void 0 + isExtensionTestHost: !!configuration.extensionTestsPath }, this.logService, this.environmentService, diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index d87a1b17431d3..59447e6c16249 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -101,4 +101,5 @@ export interface IWindowSettings { autoDetectHighContrast: boolean; menuBarVisibility: MenuBarVisibility; newWindowDimensions: 'default' | 'inherit' | 'maximized' | 'fullscreen'; + nativeTabs: boolean; } diff --git a/src/vs/workbench/browser/media/part.css b/src/vs/workbench/browser/media/part.css index b72551c0130fd..b6324fddcbb5f 100644 --- a/src/vs/workbench/browser/media/part.css +++ b/src/vs/workbench/browser/media/part.css @@ -53,7 +53,8 @@ .monaco-workbench > .part > .title > .title-actions .action-label { display: block; height: 35px; - width: 28px; + line-height: 35px; + min-width: 28px; background-size: 16px; background-position: center center; background-repeat: no-repeat; diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index 08f811aef29b0..1bdf00b4d5ae3 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -34,6 +34,7 @@ export class NoTabsTitleControl extends TitleControl { // Detect mouse click this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, DOM.EventType.CLICK, (e: MouseEvent) => this.onTitleClick(e))); + this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, DOM.EventType.AUXCLICK, (e: MouseEvent) => this.onTitleClick(e))); // Editor Label this.editorLabel = this.instantiationService.createInstance(EditorLabel, this.titleContainer, void 0); diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 3624b7f9097e2..887cf841571fd 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -8,6 +8,7 @@ import { Registry } from 'vs/platform/platform'; import nls = require('vs/nls'); import product from 'vs/platform/node/product'; +import * as os from 'os'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry'; @@ -269,6 +270,15 @@ if (isMacintosh) { 'default': 'custom', 'description': nls.localize('titleBarStyle', "Adjust the appearance of the window title bar. Changes require a full restart to apply.") }; + + // macOS Sierra (10.12.x = darwin 16.x) only + if (os.release().indexOf('16.') === 0) { + properties['window.nativeTabs'] = { + 'type': 'boolean', + 'default': false, + '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.") + }; + } } configurationRegistry.registerConfiguration({ diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index b609fb275de23..73ce709c5a22a 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -673,11 +673,16 @@ export class Workbench implements IPartService { } const windowConfig = this.configurationService.getConfiguration(); + if (windowConfig && windowConfig.window) { + const useNativeTabs = windowConfig.window.nativeTabs; + if (useNativeTabs) { + return null; // native tabs on sierra do not work with custom title style + } - const style = windowConfig && windowConfig.window && windowConfig.window.titleBarStyle; - - if (style === 'custom') { - return style; + const style = windowConfig.window.titleBarStyle; + if (style === 'custom') { + return style; + } } return null; diff --git a/src/vs/workbench/parts/files/browser/fileActions.contribution.ts b/src/vs/workbench/parts/files/browser/fileActions.contribution.ts index 348ad26288894..ef60615a0292f 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.contribution.ts @@ -10,9 +10,8 @@ import { Action, IAction } from 'vs/base/common/actions'; import { isMacintosh } from 'vs/base/common/platform'; import { ActionItem, BaseActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions, ActionBarContributor } from 'vs/workbench/browser/actionBarRegistry'; -import { IEditorInputActionContext, IEditorInputAction, EditorInputActionContributor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { GlobalNewUntitledFileAction, SaveFileAsAction, OpenFileAction, ShowOpenedFileInNewWindow, CopyPathAction, GlobalCopyPathAction, RevealInOSAction, GlobalRevealInOSAction, pasteIntoFocusedFilesExplorerViewItem, FocusOpenEditorsView, FocusFilesExplorer, GlobalCompareResourcesAction, GlobalNewFileAction, GlobalNewFolderAction, RevertFileAction, SaveFilesAction, SaveAllAction, SaveFileAction, MoveFileToTrashAction, TriggerRenameFileAction, PasteFileAction, CopyFileAction, SelectResourceForCompareAction, CompareResourcesAction, NewFolderAction, NewFileAction, OpenToSideAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView } from 'vs/workbench/parts/files/browser/fileActions'; -import { RevertLocalChangesAction, AcceptLocalChangesAction, CONFLICT_RESOLUTION_SCHEME } from 'vs/workbench/parts/files/browser/saveErrorHandler'; +import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTION_CONTEXT } from 'vs/workbench/parts/files/browser/saveErrorHandler'; import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -20,8 +19,6 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { FileStat } from 'vs/workbench/parts/files/common/explorerViewModel'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; -import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { OpenFolderAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/fileActions'; import { copyFocusedFilesExplorerViewItem, revealInOSFocusedFilesExplorerItem, openFocusedExplorerItemSideBySideCommand, copyPathOfFocusedExplorerItem, copyPathCommand, revealInExplorerCommand, revealInOSCommand, openFolderPickerCommand, openWindowCommand, openFileInNewWindowCommand, deleteFocusedFilesExplorerViewItemCommand, moveFocusedFilesExplorerViewItemToTrashCommand, renameFocusedFilesExplorerViewItemCommand } from 'vs/workbench/parts/files/browser/fileCommands'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; @@ -174,38 +171,11 @@ class ExplorerViewersActionContributor extends ActionBarContributor { } } -class ConflictResolutionActionContributor extends EditorInputActionContributor { - - constructor( @IInstantiationService private instantiationService: IInstantiationService) { - super(); - } - - public hasActionsForEditorInput(context: IEditorInputActionContext): boolean { - if (context.input instanceof DiffEditorInput && context.input.originalInput instanceof ResourceEditorInput) { - const resource = context.input.originalInput.getResource(); - - return resource && resource.scheme === CONFLICT_RESOLUTION_SCHEME; // scheme used for conflict resolution - } - - return false; - } - - public getActionsForEditorInput(context: IEditorInputActionContext): IEditorInputAction[] { - return [ - this.instantiationService.createInstance(AcceptLocalChangesAction), - this.instantiationService.createInstance(RevertLocalChangesAction) - ]; - } -} - // Contribute to Viewers that show Files const actionBarRegistry = Registry.as(ActionBarExtensions.Actionbar); actionBarRegistry.registerActionBarContributor(Scope.VIEWER, FilesViewerActionContributor); actionBarRegistry.registerActionBarContributor(Scope.VIEWER, ExplorerViewersActionContributor); -// Contribute to Conflict Editor Inputs -actionBarRegistry.registerActionBarContributor(Scope.EDITOR, ConflictResolutionActionContributor); - // Contribute Global Actions const category = nls.localize('filesCategory', "Files"); @@ -340,4 +310,22 @@ function appendEditorTitleContextMenuItem(id: string, title: string, command: IC when: ContextKeyExpr.equals('resourceScheme', 'file'), group: '2_files' }); +} + +// Editor Title Menu for Conflict Resolution +appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use local changes and overwrite disk contents"), 'save-conflict-action-accept-changes', -10, acceptLocalChangesCommand); +appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard local changes and revert to content on disk"), 'save-conflict-action-revert-changes', -9, revertLocalChangesCommand); + +function appendSaveConflictEditorTitleAction(id: string, title: string, iconClass: string, order: number, command: ICommandHandler): void { + + // Command + CommandsRegistry.registerCommand(id, command); + + // Action + MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { id, title, iconClass }, + when: ContextKeyExpr.equals(CONFLICT_RESOLUTION_CONTEXT, true), + group: 'navigation', + order + }); } \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/media/fileactions.css b/src/vs/workbench/parts/files/browser/media/fileactions.css index 5f22d89f243ff..f064ef66e0c9f 100644 --- a/src/vs/workbench/parts/files/browser/media/fileactions.css +++ b/src/vs/workbench/parts/files/browser/media/fileactions.css @@ -75,19 +75,19 @@ background-image: url('split-editor-horizontal-inverse.svg'); } -.monaco-workbench .conflict-editor-action.accept-changes { +.monaco-workbench .save-conflict-action-accept-changes { background: url('check.svg') center center no-repeat; } -.vs-dark .monaco-workbench .conflict-editor-action.accept-changes { +.vs-dark .monaco-workbench .save-conflict-action-accept-changes { background: url('check-inverse.svg') center center no-repeat; } -.monaco-workbench .conflict-editor-action.revert-changes { +.monaco-workbench .save-conflict-action-revert-changes { background: url('undo.svg') center center no-repeat; } -.vs-dark .monaco-workbench .conflict-editor-action.revert-changes { +.vs-dark .monaco-workbench .save-conflict-action-revert-changes { background: url('undo-inverse.svg') center center no-repeat; } diff --git a/src/vs/workbench/parts/files/browser/saveErrorHandler.ts b/src/vs/workbench/parts/files/browser/saveErrorHandler.ts index d4c893bda6dce..6eff064a5ecca 100644 --- a/src/vs/workbench/parts/files/browser/saveErrorHandler.ts +++ b/src/vs/workbench/parts/files/browser/saveErrorHandler.ts @@ -11,13 +11,12 @@ import { toErrorMessage } from 'vs/base/common/errorMessage'; import paths = require('vs/base/common/paths'); import { Action } from 'vs/base/common/actions'; import URI from 'vs/base/common/uri'; -import { EditorInputAction } from 'vs/workbench/browser/parts/editor/baseEditor'; import { SaveFileAsAction, RevertFileAction, SaveFileAction } from 'vs/workbench/parts/files/browser/fileActions'; import { IFileOperationResult, FileOperationResult } from 'vs/platform/files/common/files'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileService, ISaveErrorHandler, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IMessageService, IMessageWithAction, Severity, CancelAction } from 'vs/platform/message/common/message'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -26,15 +25,20 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { ITextModelResolverService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { IModel } from 'vs/editor/common/editorCommon'; -import { toResource } from 'vs/workbench/common/editor'; import { ResourceMap } from 'vs/base/common/map'; +import { IEditorGroupService } from "vs/workbench/services/group/common/groupService"; +import { DiffEditorInput } from "vs/workbench/common/editor/diffEditorInput"; +import { ResourceEditorInput } from "vs/workbench/common/editor/resourceEditorInput"; +import { IContextKeyService, IContextKey, RawContextKey } from "vs/platform/contextkey/common/contextkey"; +export const CONFLICT_RESOLUTION_CONTEXT = 'saveConflictResolutionContext'; export const CONFLICT_RESOLUTION_SCHEME = 'conflictResolution'; // A handler for save error happening with conflict resolution actions export class SaveErrorHandler implements ISaveErrorHandler, IWorkbenchContribution, ITextModelContentProvider { private messages: ResourceMap<() => void>; private toUnbind: IDisposable[]; + private conflictResolutionContext: IContextKey; constructor( @IMessageService private messageService: IMessageService, @@ -42,9 +46,13 @@ export class SaveErrorHandler implements ISaveErrorHandler, IWorkbenchContributi @ITextModelResolverService private textModelResolverService: ITextModelResolverService, @IModelService private modelService: IModelService, @IModeService private modeService: IModeService, - @IInstantiationService private instantiationService: IInstantiationService + @IInstantiationService private instantiationService: IInstantiationService, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @IContextKeyService contextKeyService: IContextKeyService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService ) { this.messages = new ResourceMap<() => void>(); + this.conflictResolutionContext = new RawContextKey(CONFLICT_RESOLUTION_CONTEXT, false).bindTo(contextKeyService); this.toUnbind = []; // Register as text model content provider that supports to load a resource as it actually @@ -80,6 +88,19 @@ export class SaveErrorHandler implements ISaveErrorHandler, IWorkbenchContributi private registerListeners(): void { this.toUnbind.push(this.textFileService.models.onModelSaved(e => this.onFileSavedOrReverted(e.resource))); this.toUnbind.push(this.textFileService.models.onModelReverted(e => this.onFileSavedOrReverted(e.resource))); + this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged())); + } + + private onEditorsChanged(): void { + let isActiveEditorSaveConflictResolution = false; + const activeEditor = this.editorService.getActiveEditor(); + + if (activeEditor && activeEditor.input instanceof DiffEditorInput && activeEditor.input.originalInput instanceof ResourceEditorInput) { + const resource = activeEditor.input.originalInput.getResource(); + isActiveEditorSaveConflictResolution = resource && resource.scheme === CONFLICT_RESOLUTION_SCHEME; + } + + this.conflictResolutionContext.set(isActiveEditorSaveConflictResolution); } private onFileSavedOrReverted(resource: URI): void { @@ -217,72 +238,64 @@ class ResolveSaveConflictMessage implements IMessageWithAction { } } -// Accept changes to resolve a conflicting edit -export class AcceptLocalChangesAction extends EditorInputAction { +export const acceptLocalChangesCommand = (accessor: ServicesAccessor, resource: URI) => { + const editorService = accessor.get(IWorkbenchEditorService); + const resolverService = accessor.get(ITextModelResolverService); - constructor( - @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @ITextModelResolverService private resolverService: ITextModelResolverService - ) { - super('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use local changes and overwrite disk contents"), 'conflict-editor-action accept-changes'); - } + const editor = editorService.getActiveEditor(); + const input = editor.input; + const position = editor.position; - public run(): TPromise { - return this.resolverService.createModelReference(toResource(this.input, { supportSideBySide: true })).then(reference => { - const model = reference.object as ITextFileEditorModel; - const localModelValue = model.getValue(); + resolverService.createModelReference(resource).then(reference => { + const model = reference.object as ITextFileEditorModel; + const localModelValue = model.getValue(); - clearPendingResolveSaveConflictMessages(); // hide any previously shown message about how to use these actions + clearPendingResolveSaveConflictMessages(); // hide any previously shown message about how to use these actions - // revert to be able to save - return model.revert().then(() => { + // revert to be able to save + return model.revert().then(() => { - // Restore user value - model.textEditorModel.setValue(localModelValue); + // Restore user value + model.textEditorModel.setValue(localModelValue); - // Trigger save - return model.save().then(() => { + // Trigger save + return model.save().then(() => { - // Reopen file input - return this.editorService.openEditor({ resource: model.getResource() }, this.position).then(() => { + // Reopen file input + return editorService.openEditor({ resource: model.getResource() }, position).then(() => { - // Clean up - this.input.dispose(); - reference.dispose(); - }); + // Clean up + input.dispose(); + reference.dispose(); }); }); }); - } -} + }); +}; -// Revert changes to resolve a conflicting edit -export class RevertLocalChangesAction extends EditorInputAction { +export const revertLocalChangesCommand = (accessor: ServicesAccessor, resource: URI) => { + const editorService = accessor.get(IWorkbenchEditorService); + const resolverService = accessor.get(ITextModelResolverService); - constructor( - @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @ITextModelResolverService private resolverService: ITextModelResolverService - ) { - super('workbench.action.files.revert', nls.localize('revertLocalChanges', "Discard local changes and revert to content on disk"), 'conflict-editor-action revert-changes'); - } + const editor = editorService.getActiveEditor(); + const input = editor.input; + const position = editor.position; - public run(): TPromise { - return this.resolverService.createModelReference(toResource(this.input, { supportSideBySide: true })).then(reference => { - const model = reference.object as ITextFileEditorModel; + resolverService.createModelReference(resource).then(reference => { + const model = reference.object as ITextFileEditorModel; - clearPendingResolveSaveConflictMessages(); // hide any previously shown message about how to use these actions + clearPendingResolveSaveConflictMessages(); // hide any previously shown message about how to use these actions - // Revert on model - return model.revert().then(() => { + // Revert on model + return model.revert().then(() => { - // Reopen file input - return this.editorService.openEditor({ resource: model.getResource() }, this.position).then(() => { + // Reopen file input + return editorService.openEditor({ resource: model.getResource() }, position).then(() => { - // Clean up - this.input.dispose(); - reference.dispose(); - }); + // Clean up + input.dispose(); + reference.dispose(); }); }); - } -} \ No newline at end of file + }); +}; \ No newline at end of file diff --git a/src/vs/workbench/parts/html/browser/webview.ts b/src/vs/workbench/parts/html/browser/webview.ts index cb24c65f1bb82..8d6673074b136 100644 --- a/src/vs/workbench/parts/html/browser/webview.ts +++ b/src/vs/workbench/parts/html/browser/webview.ts @@ -60,6 +60,9 @@ export default class Webview { this._webview.style.opacity = '0'; this._webview.autoSize = 'on'; + // disable auxclick events (see https://developers.google.com/web/updates/2016/10/auxclick) + this._webview.setAttribute('disableblinkfeatures', 'Auxclick'); + this._webview.preload = require.toUrl('./webview-pre.js'); this._webview.src = require.toUrl('./webview.html'); diff --git a/src/vs/workbench/parts/output/test/bufferedContent.test.ts b/src/vs/workbench/parts/output/test/bufferedContent.test.ts index 7f131d5479aa0..f94f61c9cc036 100644 --- a/src/vs/workbench/parts/output/test/bufferedContent.test.ts +++ b/src/vs/workbench/parts/output/test/bufferedContent.test.ts @@ -35,7 +35,8 @@ suite('Workbench - Output Buffered Content', () => { assert.equal(bufferedContent.getDelta(secondDelta).value, 'fourthfifth'); }); - test('Buffered Content - Lots of Output', () => { + test('Buffered Content - Lots of Output', function () { + this.timeout(10000); const bufferedContent = new BufferedContent(); bufferedContent.append('first line'); const firstDelta = bufferedContent.getDelta(); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index d8366265c1498..8991cb1890973 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -129,14 +129,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private onFileChanges(e: FileChangesEvent): void { - // Handle added if we are in orphan mode - if (this.inOrphanMode && e.contains(this.resource, FileChangeType.ADDED)) { - this.setOrphaned(false); - } - - // Handle deletes - if (!this.inOrphanMode && e.contains(this.resource, FileChangeType.DELETED)) { - this.setOrphaned(true); + // Track ADD and DELETES for updates of this model to orphan-mode + const newInOrphanMode = e.contains(this.resource, FileChangeType.DELETED) && !e.contains(this.resource, FileChangeType.ADDED); + if (this.inOrphanMode !== newInOrphanMode) { + this.setOrphaned(newInOrphanMode); } } diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts index 151336b09b7ab..81fac6bf367f2 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts @@ -244,7 +244,7 @@ suite('Files - TextFileEditorModel', () => { model.load().done(() => { model.textEditorModel.setValue('foo'); - return TPromise.timeout(50).then(() => { + return TPromise.timeout(100).then(() => { assert.ok(!model.isDirty()); assert.equal(eventCounter, 2);