diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 104bad5c7c1..b8f356a1e77 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -30,6 +30,7 @@ var commit = util.getVersion(root); var dependencies = Object.keys(shrinkwrap.dependencies); var baseModules = Object.keys(process.binding('natives')).filter(function (n) { return !/^_|\//.test(n); }); var nodeModules = ['electron'].concat(dependencies).concat(baseModules); +nodeModules.push('original-fs'); // provided by electron to have original fs functionality without ASAR support // Build diff --git a/extensions/typescript/src/features/bufferSyncSupport.ts b/extensions/typescript/src/features/bufferSyncSupport.ts index b51fae64f3a..7add4446ab8 100644 --- a/extensions/typescript/src/features/bufferSyncSupport.ts +++ b/extensions/typescript/src/features/bufferSyncSupport.ts @@ -4,9 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import * as fs from 'fs'; -import * as path from 'path'; - import { workspace, TextDocument, TextDocumentChangeEvent, TextDocumentContentChangeEvent, Disposable } from 'vscode'; import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; @@ -83,7 +80,6 @@ export default class BufferSyncSupport { private diagnostics: Diagnostics; private disposables: Disposable[] = []; private syncedBuffers: Map; - private closedFiles: Map; private projectValidationRequested: boolean; @@ -104,7 +100,6 @@ export default class BufferSyncSupport { this.diagnosticDelayer = new Delayer(100); this.syncedBuffers = Object.create(null); - this.closedFiles = Object.create(null); } public listen(): void { @@ -112,19 +107,6 @@ export default class BufferSyncSupport { workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, this.disposables); workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, this.disposables); workspace.textDocuments.forEach(this.onDidOpenTextDocument, this); - workspace.createFileSystemWatcher('**/*', true, true, false).onDidDelete((resource) => { - let filepath = this.client.asAbsolutePath(resource); - if (!filepath) { - return; - } - if (!this.syncedBuffers[filepath]) { - // The file is not synced (open in an editor) and got - // removed from disk. Make sure it is not in the closedFiles - // list since we shouldn't revalidate it. - delete this.closedFiles[filepath]; - this.diagnostics.delete(filepath); - } - }); } public get validate(): boolean { @@ -165,7 +147,6 @@ export default class BufferSyncSupport { } let syncedBuffer = new SyncedBuffer(document, filepath, this, this.client); this.syncedBuffers[filepath] = syncedBuffer; - delete this.closedFiles[filepath]; syncedBuffer.open(); this.requestDiagnostic(filepath); } @@ -179,14 +160,7 @@ export default class BufferSyncSupport { if (!syncedBuffer) { return; } - // If the file still exists on disk keep on validating the file. - if (fs.existsSync(filepath) && this.extensions[path.extname(filepath)]) { - this.closedFiles[filepath] = true; - } else { - // Ensure we don't have the file in the map and clear all errors. - delete this.closedFiles[filepath]; - this.diagnostics.delete(filepath); - } + this.diagnostics.delete(filepath); delete this.syncedBuffers[filepath]; syncedBuffer.close(); } @@ -246,13 +220,6 @@ export default class BufferSyncSupport { } }); - // Now add all files that we have requested diagnostics for but are now - // closed. Otherwise it might be confusing that interfile dependent markers - // don't get fixed. - Object.keys(this.closedFiles).forEach((file) => { - files.push(file); - }); - let args: Proto.GeterrRequestArgs = { delay: 0, files: files diff --git a/extensions/typescript/src/typescriptService.ts b/extensions/typescript/src/typescriptService.ts index d2d66153304..c6827c9d995 100644 --- a/extensions/typescript/src/typescriptService.ts +++ b/extensions/typescript/src/typescriptService.ts @@ -41,5 +41,6 @@ export interface ITypescriptServiceClient { execute(command:'occurrences', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; execute(command:'projectInfo', args: Proto.ProjectInfoRequestArgs, token?: CancellationToken): Promise; execute(command:'reloadProjects', args: any, expectedResult:boolean, token?: CancellationToken): Promise; + execute(command:'reload', args: Proto.ReloadRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise; execute(command:string, args:any, expectedResult:boolean| CancellationToken, token?: CancellationToken):Promise; } \ No newline at end of file diff --git a/package.json b/package.json index bb39857bd6e..be1d12384f2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "code-oss-dev", - "version": "1.3.0", + "version": "1.4.0", "electronVersion": "1.2.6", "author": { "name": "Microsoft Corporation" diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts index 1afae190aba..f34430c5908 100644 --- a/src/typings/electron.d.ts +++ b/src/typings/electron.d.ts @@ -4999,6 +4999,12 @@ declare module 'electron' { export = electron; } +declare module 'original-fs' { + import * as fs from 'fs'; + + export = fs; +} + // interface NodeRequireFunction { // (moduleName: 'electron'): Electron.ElectronMainAndRenderer; // } \ No newline at end of file diff --git a/src/vs/base/browser/dnd.ts b/src/vs/base/browser/dnd.ts index 766bf046cd3..7f8365773a5 100644 --- a/src/vs/base/browser/dnd.ts +++ b/src/vs/base/browser/dnd.ts @@ -42,17 +42,19 @@ export class DelayedDragHandler { } } -export function extractResources(e: DragEvent): URI[] { +export function extractResources(e: DragEvent, externalOnly?: boolean): URI[] { const resources: URI[] = []; if (e.dataTransfer.types.length > 0) { // Check for in-app DND - const rawData = e.dataTransfer.getData(e.dataTransfer.types[0]); - if (rawData) { - try { - resources.push(URI.parse(rawData)); - } catch (error) { - // Invalid URI + if (!externalOnly) { + const rawData = e.dataTransfer.getData(e.dataTransfer.types[0]); + if (rawData) { + try { + resources.push(URI.parse(rawData)); + } catch (error) { + // Invalid URI + } } } diff --git a/src/vs/base/browser/ui/actionbar/actionbar.css b/src/vs/base/browser/ui/actionbar/actionbar.css index b43716ae344..a5b43fe4546 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.css +++ b/src/vs/base/browser/ui/actionbar/actionbar.css @@ -141,31 +141,4 @@ flex: 1; max-width: 170px; min-width: 60px; -} - -.monaco-workbench .action-bar-select { - width: 100%; - height: 20px; - margin-top: 8px; /* Center the select box */ -} - -.monaco-workbench .action-bar-select.windows { - margin-top: 7px; /* Center the select box */ -} - -.vs-dark .monaco-workbench .action-bar-select { - background-color: #3C3C3C; - border-color: #3C3C3C; - color: rgb(204, 204, 204); -} - -.hc-black .monaco-workbench .action-bar-select { - background-color: #3C3C3C; - border-color: #3C3C3C; - color: white; -} - -.vs .monaco-workbench .action-bar-select { - background-color: white; - border-color: #CECECE; } \ No newline at end of file diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 2f5af7cd36b..339afe226aa 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -11,11 +11,11 @@ import lifecycle = require('vs/base/common/lifecycle'); import {Promise} from 'vs/base/common/winjs.base'; import {Builder, $} from 'vs/base/browser/builder'; import platform = require('vs/base/common/platform'); -import {IAction, IActionRunner, Action, ActionRunner} from 'vs/base/common/actions'; +import {IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner} from 'vs/base/common/actions'; import DOM = require('vs/base/browser/dom'); import {EventType as CommonEventType} from 'vs/base/common/events'; import types = require('vs/base/common/types'); -import {IEventEmitter, EventEmitter, EmitterEvent} from 'vs/base/common/eventEmitter'; +import {IEventEmitter, EventEmitter} from 'vs/base/common/eventEmitter'; import {Gesture, EventType} from 'vs/base/browser/touch'; import {StandardKeyboardEvent} from 'vs/base/browser/keyboardEvent'; import {CommonKeybindings} from 'vs/base/common/keyCodes'; @@ -48,38 +48,33 @@ export class BaseActionItem extends EventEmitter implements IActionItem { this._action = action; if (action instanceof Action) { - let l = (action).addBulkListener2((events: EmitterEvent[]) => { + this._callOnDispose.push(action.onDidChange(event => { if (!this.builder) { // we have not been rendered yet, so there // is no point in updating the UI return; } + this._handleActionChangeEvent(event); + })); + } + } - events.forEach((event: EmitterEvent) => { - switch (event.getType()) { - case Action.ENABLED: - this._updateEnabled(); - break; - case Action.LABEL: - this._updateLabel(); - this._updateTooltip(); - break; - case Action.TOOLTIP: - this._updateTooltip(); - break; - case Action.CLASS: - this._updateClass(); - break; - case Action.CHECKED: - this._updateChecked(); - break; - default: - this._updateUnknown(event); - break; - } - }); - }); - this._callOnDispose.push(l); + protected _handleActionChangeEvent(event: IActionChangeEvent): void { + if (event.enabled !== void 0) { + this._updateEnabled(); + } + if (event.checked !== void 0) { + this._updateChecked(); + } + if (event.class !== void 0) { + this._updateClass(); + } + if (event.label !== void 0) { + this._updateLabel(); + this._updateTooltip(); + } + if (event.tooltip !== void 0) { + this._updateTooltip(); } } @@ -161,30 +156,26 @@ export class BaseActionItem extends EventEmitter implements IActionItem { } } - public _updateEnabled(): void { + protected _updateEnabled(): void { // implement in subclass } - public _updateLabel(): void { + protected _updateLabel(): void { // implement in subclass } - public _updateTooltip(): void { + protected _updateTooltip(): void { // implement in subclass } - public _updateClass(): void { + protected _updateClass(): void { // implement in subclass } - public _updateChecked(): void { + protected _updateChecked(): void { // implement in subclass } - public _updateUnknown(event: EmitterEvent): void { - // can implement in subclass - } - public dispose(): void { super.dispose(); @@ -746,7 +737,7 @@ export class SelectActionItem extends BaseActionItem { super(ctx, action); this.select = document.createElement('select'); - this.select.className = `action-bar-select ${platform.isWindows ? 'windows' : ''}`; + this.select.className = 'action-bar-select'; this.options = options; this.selected = selected; diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index 3caa1b0749c..2cd7282a5e4 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -52,22 +52,22 @@ export class Button extends EventEmitter { }); } - public getElement(): HTMLElement { + getElement(): HTMLElement { return this.$el.getHTMLElement(); } - public set label(value: string) { + set label(value: string) { if (!this.$el.hasClass('monaco-text-button')) { this.$el.addClass('monaco-text-button'); } this.$el.text(value); } - public set icon(iconClassName: string) { + set icon(iconClassName: string) { this.$el.addClass(iconClassName); } - public set enabled(value: boolean) { + set enabled(value: boolean) { if (value) { this.$el.removeClass('disabled'); this.$el.attr({ @@ -81,11 +81,15 @@ export class Button extends EventEmitter { } } - public get enabled() { + get enabled() { return !this.$el.hasClass('disabled'); } - public dispose(): void { + focus(): void { + this.$el.domFocus(); + } + + dispose(): void { if (this.$el) { this.$el.dispose(); this.$el = null; diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 124c4ce869a..73b01f17329 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -136,7 +136,7 @@ export class InputBox extends Widget { } } - setTimeout(() => this.layout(), 0); + setTimeout(() => this.updateMirror(), 0); // Support actions if (this.options.actions) { @@ -357,19 +357,25 @@ export class InputBox extends Widget { this._onDidChange.fire(this.value); this.validate(); - - if (this.mirror) { - let lastCharCode = this.value.charCodeAt(this.value.length - 1); - let suffix = lastCharCode === 10 ? ' ' : ''; - this.mirror.textContent = this.value + suffix; - this.layout(); - } + this.updateMirror(); if (this.state === 'open') { this.contextViewProvider.layout(); } } + private updateMirror(): void { + if (!this.mirror) { + return; + } + + const value = this.value || this.placeholder; + let lastCharCode = value.charCodeAt(value.length - 1); + let suffix = lastCharCode === 10 ? ' ' : ''; + this.mirror.textContent = value + suffix; + this.layout(); + } + public layout(): void { if (!this.mirror) { return; diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index 840d99fec32..b1a150eaa9a 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -8,6 +8,7 @@ import {TPromise} from 'vs/base/common/winjs.base'; import { IEventEmitter, EventEmitter } from 'vs/base/common/eventEmitter'; import {IDisposable} from 'vs/base/common/lifecycle'; import * as Events from 'vs/base/common/events'; +import Event, {Emitter} from 'vs/base/common/event'; export interface IAction extends IDisposable { id: string; @@ -68,26 +69,27 @@ export interface IActionProvider { getAction(id: string): IAction; } -export class Action extends EventEmitter implements IAction { +export interface IActionChangeEvent { + label?: string; + tooltip?: string; + class?: string; + enabled?: boolean; + checked?: boolean; +} - static LABEL: string = 'label'; - static TOOLTIP: string = 'tooltip'; - static CLASS: string = 'class'; - static ENABLED: string = 'enabled'; - static CHECKED: string = 'checked'; +export class Action implements IAction { - public _id: string; - public _label: string; - public _tooltip: string; - public _cssClass: string; - public _enabled: boolean; - public _checked: boolean; - public _actionCallback: IActionCallback; - public _order: number; - - constructor(id: string, label = '', cssClass = '', enabled = true, actionCallback: IActionCallback = null) { - super(); + protected _onDidChange = new Emitter(); + protected _id: string; + protected _label: string; + protected _tooltip: string; + protected _cssClass: string; + protected _enabled: boolean; + protected _checked: boolean; + protected _actionCallback: IActionCallback; + protected _order: number; + constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback: IActionCallback = null) { this._id = id; this._label = label; this._cssClass = cssClass; @@ -95,6 +97,14 @@ export class Action extends EventEmitter implements IAction { this._actionCallback = actionCallback; } + public dispose() { + this._onDidChange.dispose(); + } + + public get onDidChange(): Event { + return this._onDidChange.event; + } + public get id(): string { return this._id; } @@ -107,10 +117,10 @@ export class Action extends EventEmitter implements IAction { this._setLabel(value); } - _setLabel(value: string): void { + protected _setLabel(value: string): void { if (this._label !== value) { this._label = value; - this.emit(Action.LABEL, { source: this }); + this._onDidChange.fire({ label: value }); } } @@ -122,10 +132,10 @@ export class Action extends EventEmitter implements IAction { this._setTooltip(value); } - _setTooltip(value: string): void { + protected _setTooltip(value: string): void { if (this._tooltip !== value) { this._tooltip = value; - this.emit(Action.TOOLTIP, { source: this }); + this._onDidChange.fire({ tooltip: value }); } } @@ -137,10 +147,10 @@ export class Action extends EventEmitter implements IAction { this._setClass(value); } - _setClass(value: string): void { + protected _setClass(value: string): void { if (this._cssClass !== value) { this._cssClass = value; - this.emit(Action.CLASS, { source: this }); + this._onDidChange.fire({ class: value }); } } @@ -152,10 +162,10 @@ export class Action extends EventEmitter implements IAction { this._setEnabled(value); } - _setEnabled(value: boolean): void { + protected _setEnabled(value: boolean): void { if (this._enabled !== value) { this._enabled = value; - this.emit(Action.ENABLED, { source: this }); + this._onDidChange.fire({ enabled: value }); } } @@ -167,10 +177,10 @@ export class Action extends EventEmitter implements IAction { this._setChecked(value); } - _setChecked(value: boolean): void { + protected _setChecked(value: boolean): void { if (this._checked !== value) { this._checked = value; - this.emit(Action.CHECKED, { source: this }); + this._onDidChange.fire({ checked: value }); } } @@ -182,14 +192,6 @@ export class Action extends EventEmitter implements IAction { this._order = value; } - public get actionCallback(): IActionCallback { - return this._actionCallback; - } - - public set actionCallback(value: IActionCallback) { - this._actionCallback = value; - } - public run(event?: any): TPromise { if (this._actionCallback !== null) { return this._actionCallback(event); diff --git a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts index f8d94989037..806f4111bba 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts @@ -603,7 +603,7 @@ export class QuickOpenWidget implements IModelProvider { this.tree.layout(); // Handle auto focus - if (!this.tree.getFocus() && input && input.entries.some(e => this.isElementVisible(input, e))) { + if (input && input.entries.some(e => this.isElementVisible(input, e))) { this.autoFocus(input, autoFocus); } }, errors.onUnexpectedError); diff --git a/src/vs/code/electron-main/env.ts b/src/vs/code/electron-main/env.ts index 36db9b4e7c7..e8a40ec8c22 100644 --- a/src/vs/code/electron-main/env.ts +++ b/src/vs/code/electron-main/env.ts @@ -6,7 +6,7 @@ 'use strict'; import * as crypto from 'crypto'; -import * as fs from 'fs'; +import * as fs from 'original-fs'; import * as path from 'path'; import * as os from 'os'; import { app } from 'electron'; @@ -263,7 +263,8 @@ function parsePathArguments(cwd: string, args: string[], gotoLineMode?: boolean) realPath = path.normalize(path.isAbsolute(pathCandidate) ? pathCandidate : path.join(cwd, pathCandidate)); } - if (!paths.isValidBasename(path.basename(realPath))) { + const basename = path.basename(realPath); + if (basename /* can be empty if code is opened on root */ && !paths.isValidBasename(basename)) { return null; // do not allow invalid file names } diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index cd78511de17..25041c383e5 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -6,7 +6,7 @@ 'use strict'; import * as nls from 'vs/nls'; -import * as fs from 'fs'; +import * as fs from 'original-fs'; import { app, ipcMain as ipc } from 'electron'; import { assign } from 'vs/base/common/objects'; import { mkdirp } from 'vs/base/node/pfs'; diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 910c9a30ec4..bcc9667b988 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -193,7 +193,7 @@ export class VSCodeMenu { // Goto let gotoMenu = new Menu(); - let gotoMenuItem = new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'mGoto', comment: ['&& denotes a mnemonic'] }, "&&Goto")), submenu: gotoMenu }); + let gotoMenuItem = new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'mGoto', comment: ['&& denotes a mnemonic'] }, "&&Go")), submenu: gotoMenu }); this.setGotoMenu(gotoMenu); // Mac: Window diff --git a/src/vs/code/electron-main/storage.ts b/src/vs/code/electron-main/storage.ts index f0b7316667b..8868f63d326 100644 --- a/src/vs/code/electron-main/storage.ts +++ b/src/vs/code/electron-main/storage.ts @@ -6,7 +6,7 @@ 'use strict'; import * as path from 'path'; -import * as fs from 'fs'; +import * as fs from 'original-fs'; import { EventEmitter } from 'events'; import { IEnvironmentService } from 'vs/code/electron-main/env'; import { ServiceIdentifier, createDecorator } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/code/electron-main/update-manager.ts b/src/vs/code/electron-main/update-manager.ts index fa24a838511..e0b416b1214 100644 --- a/src/vs/code/electron-main/update-manager.ts +++ b/src/vs/code/electron-main/update-manager.ts @@ -5,7 +5,7 @@ 'use strict'; -import * as fs from 'fs'; +import * as fs from 'original-fs'; import * as path from 'path'; import * as electron from 'electron'; import * as platform from 'vs/base/common/platform'; diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 19aeb780bd2..98e8c379e6c 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -6,7 +6,7 @@ 'use strict'; import * as path from 'path'; -import * as fs from 'fs'; +import * as fs from 'original-fs'; import * as platform from 'vs/base/common/platform'; import * as nls from 'vs/nls'; import * as paths from 'vs/base/common/paths'; diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index dea192101df..4ea0eadc972 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -32,7 +32,7 @@ import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppen const notFound = id => localize('notFound', "Extension '{0}' not found.", id); const notInstalled = id => localize('notInstalled', "Extension '{0}' is not installed.", id); -const useId = localize('useId', "Make sure you use the full extension ID, eg: {0}", 'ms-vscode.csharp'); +const useId = localize('useId', "Make sure you use the full extension ID, including the publisher, eg: {0}", 'ms-vscode.csharp'); function getId(manifest: IExtensionManifest): string { return `${ manifest.publisher }.${ manifest.name }`; diff --git a/src/vs/editor/browser/editor.all.ts b/src/vs/editor/browser/editor.all.ts index 95c67df5aeb..0f4f27a70e0 100644 --- a/src/vs/editor/browser/editor.all.ts +++ b/src/vs/editor/browser/editor.all.ts @@ -26,7 +26,6 @@ import 'vs/editor/contrib/linesOperations/common/linesOperations'; import 'vs/editor/contrib/carretOperations/common/carretOperations'; import 'vs/editor/contrib/links/browser/links'; import 'vs/editor/contrib/multicursor/common/multicursor'; -import 'vs/editor/contrib/outlineMarker/browser/outlineMarker'; import 'vs/editor/contrib/parameterHints/browser/parameterHints'; import 'vs/editor/contrib/quickFix/browser/quickFix'; import 'vs/editor/contrib/referenceSearch/browser/referenceSearch'; diff --git a/src/vs/editor/browser/standalone/standaloneEditor.ts b/src/vs/editor/browser/standalone/standaloneEditor.ts index fddf853ee19..5b1360d49ce 100644 --- a/src/vs/editor/browser/standalone/standaloneEditor.ts +++ b/src/vs/editor/browser/standalone/standaloneEditor.ts @@ -437,6 +437,7 @@ export function createMonacoEditorAPI(): typeof monaco.editor { // consts KEYBINDING_CONTEXT_EDITOR_TEXT_FOCUS: editorCommon.KEYBINDING_CONTEXT_EDITOR_TEXT_FOCUS, KEYBINDING_CONTEXT_EDITOR_FOCUS: editorCommon.KEYBINDING_CONTEXT_EDITOR_FOCUS, + KEYBINDING_CONTEXT_EDITOR_READONLY: editorCommon.KEYBINDING_CONTEXT_EDITOR_READONLY, KEYBINDING_CONTEXT_EDITOR_HAS_MULTIPLE_SELECTIONS: editorCommon.KEYBINDING_CONTEXT_EDITOR_HAS_MULTIPLE_SELECTIONS, KEYBINDING_CONTEXT_EDITOR_HAS_NON_EMPTY_SELECTION: editorCommon.KEYBINDING_CONTEXT_EDITOR_HAS_NON_EMPTY_SELECTION, KEYBINDING_CONTEXT_EDITOR_LANGUAGE_ID: editorCommon.KEYBINDING_CONTEXT_EDITOR_LANGUAGE_ID, diff --git a/src/vs/editor/common/commonCodeEditor.ts b/src/vs/editor/common/commonCodeEditor.ts index 69fb2842d10..2ffeb9f41a8 100644 --- a/src/vs/editor/common/commonCodeEditor.ts +++ b/src/vs/editor/common/commonCodeEditor.ts @@ -113,6 +113,7 @@ export abstract class CommonCodeEditor extends EventEmitter implements IActionPr private _editorIdContextKey: IKeybindingContextKey; protected _editorFocusContextKey: IKeybindingContextKey; private _editorTabMovesFocusKey: IKeybindingContextKey; + private _editorReadonly: IKeybindingContextKey; private _hasMultipleSelectionsKey: IKeybindingContextKey; private _hasNonEmptySelectionKey: IKeybindingContextKey; private _langIdKey: IKeybindingContextKey; @@ -141,6 +142,7 @@ export abstract class CommonCodeEditor extends EventEmitter implements IActionPr this._editorIdContextKey = this._keybindingService.createKey('editorId', this.getId()); this._editorFocusContextKey = this._keybindingService.createKey(editorCommon.KEYBINDING_CONTEXT_EDITOR_FOCUS, undefined); this._editorTabMovesFocusKey = this._keybindingService.createKey(editorCommon.KEYBINDING_CONTEXT_EDITOR_TAB_MOVES_FOCUS, false); + this._editorReadonly = this._keybindingService.createKey(editorCommon.KEYBINDING_CONTEXT_EDITOR_READONLY, false); this._hasMultipleSelectionsKey = this._keybindingService.createKey(editorCommon.KEYBINDING_CONTEXT_EDITOR_HAS_MULTIPLE_SELECTIONS, false); this._hasNonEmptySelectionKey = this._keybindingService.createKey(editorCommon.KEYBINDING_CONTEXT_EDITOR_HAS_NON_EMPTY_SELECTION, false); this._langIdKey = this._keybindingService.createKey(editorCommon.KEYBINDING_CONTEXT_EDITOR_LANGUAGE_ID, undefined); @@ -157,7 +159,15 @@ export abstract class CommonCodeEditor extends EventEmitter implements IActionPr if (this._configuration.editor.tabFocusMode) { this._editorTabMovesFocusKey.set(true); } - this._lifetimeDispose.push(this._configuration.onDidChange((e) => this.emit(editorCommon.EventType.ConfigurationChanged, e))); + this._editorReadonly.set(this._configuration.editor.readOnly); + this._lifetimeDispose.push(this._configuration.onDidChange((e) => { + if (this._configuration.editor.tabFocusMode) { + this._editorTabMovesFocusKey.set(true); + } else { + this._editorTabMovesFocusKey.reset(); + } + this.emit(editorCommon.EventType.ConfigurationChanged, e); + })); this._telemetryService = telemetryService; this._instantiationService = instantiationService.createChild(new ServiceCollection([IKeybindingService, this._keybindingService])); @@ -212,11 +222,7 @@ export abstract class CommonCodeEditor extends EventEmitter implements IActionPr public updateOptions(newOptions:editorCommon.IEditorOptions): void { this._configuration.updateOptions(newOptions); - if (this._configuration.editor.tabFocusMode) { - this._editorTabMovesFocusKey.set(true); - } else { - this._editorTabMovesFocusKey.reset(); - } + this._editorReadonly.set(this._configuration.editor.readOnly); } public getConfiguration(): editorCommon.InternalEditorOptions { @@ -708,6 +714,12 @@ export abstract class CommonCodeEditor extends EventEmitter implements IActionPr if (oldDecorationsIds) { this.deltaDecorations(oldDecorationsIds, []); } + if (this._decorationTypeKeysToIds.hasOwnProperty(decorationTypeKey)) { + delete this._decorationTypeKeysToIds[decorationTypeKey]; + } + if (this._decorationTypeSubtypes.hasOwnProperty(decorationTypeKey)) { + delete this._decorationTypeSubtypes[decorationTypeKey]; + } } public addTypingListener(character:string, callback: () => void): IDisposable { diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 150c52a936d..aa386919c4b 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -55,6 +55,38 @@ export const EditorZoom: IEditorZoom = new class { } }; +/** + * Control what pressing Tab does. + * If it is false, pressing Tab or Shift-Tab will be handled by the editor. + * If it is true, pressing Tab or Shift-Tab will move the browser focus. + * Defaults to false. + */ +export interface ITabFocus { + onDidChangeTabFocus:Event; + getTabFocusMode(): boolean; + setTabFocusMode(tabFocusMode:boolean): void; +} + +export const TabFocus: ITabFocus = new class { + private _tabFocus: boolean = false; + + private _onDidChangeTabFocus: Emitter = new Emitter(); + public onDidChangeTabFocus:Event = this._onDidChangeTabFocus.event; + + public getTabFocusMode(): boolean { + return this._tabFocus; + } + + public setTabFocusMode(tabFocusMode:boolean): void { + if (this._tabFocus === tabFocusMode) { + return; + } + + this._tabFocus = tabFocusMode; + this._onDidChangeTabFocus.fire(this._tabFocus); + } +}; + /** * Experimental screen reader support toggle */ @@ -188,7 +220,7 @@ class InternalEditorOptionsHelper { let readOnly = toBoolean(opts.readOnly); - let tabFocusMode = toBoolean(opts.tabFocusMode); + let tabFocusMode = TabFocus.getTabFocusMode(); if (readOnly) { tabFocusMode = true; } @@ -230,7 +262,6 @@ class InternalEditorOptionsHelper { suggestOnTriggerCharacters: toBoolean(opts.suggestOnTriggerCharacters), acceptSuggestionOnEnter: toBoolean(opts.acceptSuggestionOnEnter), selectionHighlight: toBoolean(opts.selectionHighlight), - outlineMarkers: toBoolean(opts.outlineMarkers), referenceInfos: toBoolean(opts.referenceInfos), folding: toBoolean(opts.folding), }); @@ -391,6 +422,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed this.editor = this._computeInternalOptions(); this.editorClone = this.editor.clone(); this._register(EditorZoom.onDidChangeZoomLevel(_ => this._recomputeOptions())); + this._register(TabFocus.onDidChangeTabFocus(_ => this._recomputeOptions())); } public dispose(): void { @@ -663,11 +695,6 @@ let editorConfiguration:IConfigurationNode = { 'default': DefaultConfig.editor.selectionHighlight, 'description': nls.localize('selectionHighlight', "Controls whether the editor should highlight similar matches to the selection") }, -// 'editor.outlineMarkers' : { -// 'type': 'boolean', -// 'default': DefaultConfig.editor.outlineMarkers, -// 'description': nls.localize('outlineMarkers', "Controls whether the editor should draw horizontal lines before classes and methods") -// }, 'editor.overviewRulerLanes' : { 'type': 'integer', 'default': 3, diff --git a/src/vs/editor/common/config/defaultConfig.ts b/src/vs/editor/common/config/defaultConfig.ts index a0085c9e4f1..2c96b01efc0 100644 --- a/src/vs/editor/common/config/defaultConfig.ts +++ b/src/vs/editor/common/config/defaultConfig.ts @@ -70,7 +70,6 @@ class ConfigClass implements IConfiguration { wordWrapBreakBeforeCharacters: '([{‘“〈《「『【〔([{「£¥$£¥++', wordWrapBreakAfterCharacters: ' \t})]?|&,;¢°′″‰℃、。。、¢,.:;?!%・・ゝゞヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻ァィゥェォャュョッー’”〉》」』】〕)]}」', wordWrapBreakObtrusiveCharacters: '.', - tabFocusMode: false, // Features hover: true, @@ -85,7 +84,6 @@ class ConfigClass implements IConfiguration { suggestOnTriggerCharacters: true, acceptSuggestionOnEnter: true, selectionHighlight: true, - outlineMarkers: false, referenceInfos: true, folding: true, renderWhitespace: false, diff --git a/src/vs/editor/common/controller/oneCursor.ts b/src/vs/editor/common/controller/oneCursor.ts index 95bd04de863..6c4a2f61ecd 100644 --- a/src/vs/editor/common/controller/oneCursor.ts +++ b/src/vs/editor/common/controller/oneCursor.ts @@ -1518,26 +1518,31 @@ export class OneCursorOp { ctx.isAutoWhitespaceCommand = true; - let typeText = ''; - if (cursor.model.getLineMaxColumn(selection.startLineNumber) === 1) { - // Line is empty => indent straight to the right place - typeText = cursor.model.normalizeIndentation(this._goodIndentForLine(cursor, selection.startLineNumber)); - } else { - let position = cursor.getPosition(); - let modelOpts = cursor.model.getOptions(); - if (modelOpts.insertSpaces) { - let visibleColumnFromColumn = cursor.getVisibleColumnFromColumn(position.lineNumber, position.column); - let tabSize = modelOpts.tabSize; - let spacesCnt = tabSize - (visibleColumnFromColumn % tabSize); - for (let i = 0; i < spacesCnt; i++) { - typeText += ' '; - } - } else { - typeText = '\t'; + let lineText = cursor.model.getLineContent(selection.startLineNumber); + + if (/^\s*$/.test(lineText)) { + let possibleTypeText = cursor.model.normalizeIndentation(this._goodIndentForLine(cursor, selection.startLineNumber)); + if (!strings.startsWith(lineText, possibleTypeText)) { + ctx.executeCommand = new ReplaceCommand(new Range(selection.startLineNumber, 1, selection.startLineNumber, lineText.length + 1), possibleTypeText); + return true; } } + let typeText = ''; + let position = cursor.getPosition(); + let modelOpts = cursor.model.getOptions(); + if (modelOpts.insertSpaces) { + let visibleColumnFromColumn = cursor.getVisibleColumnFromColumn(position.lineNumber, position.column); + let tabSize = modelOpts.tabSize; + let spacesCnt = tabSize - (visibleColumnFromColumn % tabSize); + for (let i = 0; i < spacesCnt; i++) { + typeText += ' '; + } + } else { + typeText = '\t'; + } + ctx.executeCommand = new ReplaceCommand(selection, typeText); return true; } else { diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 9c9e471ad13..cba6dda3700 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -328,14 +328,6 @@ export interface IEditorOptions { */ wordWrapBreakObtrusiveCharacters?: string; - /** - * Control what pressing Tab does. - * If it is false, pressing Tab or Shift-Tab will be handled by the editor. - * If it is true, pressing Tab or Shift-Tab will move the browser focus. - * Defaults to false. - */ - tabFocusMode?:boolean; - /** * Performance guard: Stop rendering a line after x characters. * Defaults to 10000 if wrappingColumn is -1. Defaults to -1 if wrappingColumn is >= 0. @@ -401,11 +393,6 @@ export interface IEditorOptions { * Defaults to true. */ selectionHighlight?:boolean; - /** - * Show lines before classes and methods (based on outline info). - * Defaults to false. - */ - outlineMarkers?: boolean; /** * Show reference infos (a.k.a. code lenses) for modes that support it * Defaults to true. @@ -811,7 +798,6 @@ export class EditorContribOptions { suggestOnTriggerCharacters: boolean; acceptSuggestionOnEnter: boolean; selectionHighlight:boolean; - outlineMarkers: boolean; referenceInfos: boolean; folding: boolean; @@ -830,7 +816,6 @@ export class EditorContribOptions { suggestOnTriggerCharacters: boolean; acceptSuggestionOnEnter: boolean; selectionHighlight:boolean; - outlineMarkers: boolean; referenceInfos: boolean; folding: boolean; }) { @@ -845,7 +830,6 @@ export class EditorContribOptions { this.suggestOnTriggerCharacters = Boolean(source.suggestOnTriggerCharacters); this.acceptSuggestionOnEnter = Boolean(source.acceptSuggestionOnEnter); this.selectionHighlight = Boolean(source.selectionHighlight); - this.outlineMarkers = Boolean(source.outlineMarkers); this.referenceInfos = Boolean(source.referenceInfos); this.folding = Boolean(source.folding); } @@ -866,7 +850,6 @@ export class EditorContribOptions { && this.suggestOnTriggerCharacters === other.suggestOnTriggerCharacters && this.acceptSuggestionOnEnter === other.acceptSuggestionOnEnter && this.selectionHighlight === other.selectionHighlight - && this.outlineMarkers === other.outlineMarkers && this.referenceInfos === other.referenceInfos && this.folding === other.folding ); @@ -3052,6 +3035,10 @@ export const KEYBINDING_CONTEXT_EDITOR_FOCUS = 'editorFocus'; * @internal */ export const KEYBINDING_CONTEXT_EDITOR_TAB_MOVES_FOCUS = 'editorTabMovesFocus'; +/** + * A context key that is set when the editor's text is readonly. + */ +export const KEYBINDING_CONTEXT_EDITOR_READONLY = 'editorReadonly'; /** * A context key that is set when the editor has multiple selections (multiple cursors). */ diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index dc07561ff73..f001193b6df 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -34,8 +34,8 @@ export interface CommentRule { } /** - * The language configuration interfaces defines the contract between extensions - * and various editor features, like automatic bracket insertion, automatic indentation etc. + * The language configuration interface defines the contract between extensions and + * various editor features, like automatic bracket insertion, automatic indentation etc. */ export interface LanguageConfiguration { /** diff --git a/src/vs/editor/common/modes/supports/suggestSupport.ts b/src/vs/editor/common/modes/supports/suggestSupport.ts index 29f6e3673c1..8e5d14999d2 100644 --- a/src/vs/editor/common/modes/supports/suggestSupport.ts +++ b/src/vs/editor/common/modes/supports/suggestSupport.ts @@ -6,7 +6,7 @@ import {IReadOnlyModel} from 'vs/editor/common/editorCommon'; import {ISuggestResult, ISuggestSupport} from 'vs/editor/common/modes'; -import {IFilter, matchesStrictPrefix, fuzzyContiguousFilter} from 'vs/base/common/filters'; +import {IFilter, matchesPrefix, fuzzyContiguousFilter} from 'vs/base/common/filters'; import {IEditorWorkerService} from 'vs/editor/common/services/editorWorkerService'; import {IConfigurationService} from 'vs/platform/configuration/common/configuration'; import {IConfigurationRegistry, Extensions} from 'vs/platform/configuration/common/configurationRegistry'; @@ -41,7 +41,7 @@ export class TextualSuggestSupport implements ISuggestSupport { } public get filter(): IFilter { - return matchesStrictPrefix; + return matchesPrefix; } private _editorWorkerService: IEditorWorkerService; diff --git a/src/vs/editor/contrib/clipboard/browser/clipboard.ts b/src/vs/editor/contrib/clipboard/browser/clipboard.ts index 580b6661eaf..075788bb41b 100644 --- a/src/vs/editor/contrib/clipboard/browser/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/browser/clipboard.ts @@ -18,6 +18,8 @@ import {EditorAction} from 'vs/editor/common/editorAction'; import {Behaviour} from 'vs/editor/common/editorActionEnablement'; import * as editorCommon from 'vs/editor/common/editorCommon'; import {CommonEditorRegistry, ContextKey, EditorActionDescriptor} from 'vs/editor/common/editorCommonExtensions'; +import {MenuRegistry} from 'vs/platform/actions/browser/menuService'; +import {MenuId} from 'vs/platform/actions/common/actions'; class ClipboardWritingAction extends EditorAction { @@ -62,7 +64,7 @@ function editorCursorIsInEditableRange(editor:editorCommon.ICommonCodeEditor): b class ExecCommandCutAction extends ClipboardWritingAction { constructor(descriptor:editorCommon.IEditorActionDescriptorData, editor:editorCommon.ICommonCodeEditor) { - super(descriptor, editor, Behaviour.Writeable | Behaviour.WidgetFocus | Behaviour.ShowInContextMenu | Behaviour.UpdateOnCursorPositionChange); + super(descriptor, editor, Behaviour.Writeable | Behaviour.WidgetFocus | Behaviour.UpdateOnCursorPositionChange); } public getGroupId(): string { @@ -83,7 +85,7 @@ class ExecCommandCutAction extends ClipboardWritingAction { class ExecCommandCopyAction extends ClipboardWritingAction { constructor(descriptor:editorCommon.IEditorActionDescriptorData, editor:editorCommon.ICommonCodeEditor) { - super(descriptor, editor, Behaviour.WidgetFocus | Behaviour.ShowInContextMenu); + super(descriptor, editor, Behaviour.WidgetFocus); } public getGroupId(): string { @@ -100,7 +102,7 @@ class ExecCommandCopyAction extends ClipboardWritingAction { class ExecCommandPasteAction extends EditorAction { constructor(descriptor:editorCommon.IEditorActionDescriptorData, editor:editorCommon.ICommonCodeEditor) { - super(descriptor, editor, Behaviour.Writeable | Behaviour.WidgetFocus | Behaviour.ShowInContextMenu | Behaviour.UpdateOnCursorPositionChange); + super(descriptor, editor, Behaviour.Writeable | Behaviour.WidgetFocus | Behaviour.UpdateOnCursorPositionChange); } public getGroupId(): string { @@ -123,8 +125,9 @@ interface IClipboardCommand extends IKeybindings { id: string; label: string; execCommand: string; + kbExpr: KbExpr; } -function registerClipboardAction(desc:IClipboardCommand, alias:string) { +function registerClipboardAction(desc: IClipboardCommand, alias: string, weight: number) { if (!browser.supportsExecCommand(desc.execCommand)) { return; } @@ -139,6 +142,17 @@ function registerClipboardAction(desc:IClipboardCommand, alias:string) { mac: desc.mac, kbExpr: KbExpr.has(editorCommon.KEYBINDING_CONTEXT_EDITOR_TEXT_FOCUS) }, alias)); + + MenuRegistry.addCommand({ + id: desc.id, + title: desc.label + }); + + MenuRegistry.appendMenuItem(MenuId.EditorContext, { + command: MenuRegistry.getCommand(desc.id), + group: `cutcopypaste@${weight}`, + when: desc.kbExpr + }); } registerClipboardAction({ @@ -147,24 +161,29 @@ registerClipboardAction({ label: nls.localize('actions.clipboard.cutLabel', "Cut"), execCommand: 'cut', primary: KeyMod.CtrlCmd | KeyCode.KEY_X, - win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, secondary: [KeyMod.Shift | KeyCode.Delete] } -}, 'Cut'); + win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, secondary: [KeyMod.Shift | KeyCode.Delete] }, + kbExpr: KbExpr.and(KbExpr.has(editorCommon.KEYBINDING_CONTEXT_EDITOR_FOCUS), KbExpr.not(editorCommon.KEYBINDING_CONTEXT_EDITOR_READONLY)) +}, 'Cut', 1); + registerClipboardAction({ ctor: ExecCommandCopyAction, id: 'editor.action.clipboardCopyAction', label: nls.localize('actions.clipboard.copyLabel', "Copy"), execCommand: 'copy', primary: KeyMod.CtrlCmd | KeyCode.KEY_C, - win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] } -}, 'Copy'); + win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] }, + kbExpr: KbExpr.has(editorCommon.KEYBINDING_CONTEXT_EDITOR_FOCUS) +}, 'Copy', 2); + registerClipboardAction({ ctor: ExecCommandPasteAction, id: 'editor.action.clipboardPasteAction', label: nls.localize('actions.clipboard.pasteLabel', "Paste"), execCommand: 'paste', primary: KeyMod.CtrlCmd | KeyCode.KEY_V, - win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] } -}, 'Paste'); + win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] }, + kbExpr: KbExpr.and(KbExpr.has(editorCommon.KEYBINDING_CONTEXT_EDITOR_FOCUS), KbExpr.not(editorCommon.KEYBINDING_CONTEXT_EDITOR_READONLY)) +}, 'Paste', 3); function execCommandToHandler(actionId: string, browserCommand: string, accessor: ServicesAccessor, args: any): void { let focusedEditor = findFocusedEditor(actionId, accessor, false); diff --git a/src/vs/editor/contrib/find/common/findController.ts b/src/vs/editor/contrib/find/common/findController.ts index 9aa6c7e3dd0..2c87498142c 100644 --- a/src/vs/editor/contrib/find/common/findController.ts +++ b/src/vs/editor/contrib/find/common/findController.ts @@ -343,14 +343,14 @@ export interface IMultiCursorFindResult { matchCase:boolean; wholeWord:boolean; - nextMatch: Selection; + currentMatch: Selection; } function multiCursorFind(editor:editorCommon.ICommonCodeEditor, changeFindSearchString:boolean): IMultiCursorFindResult { let controller = CommonFindController.getFindController(editor); let state = controller.getState(); - let searchText: string, - nextMatch: Selection; + let searchText: string; + let currentMatch: Selection; // In any case, if the find widget was ever opened, the options are taken from it let wholeWord = state.wholeWord; @@ -379,7 +379,7 @@ function multiCursorFind(editor:editorCommon.ICommonCodeEditor, changeFindSearch return null; } searchText = word.word; - nextMatch = new Selection(s.startLineNumber, word.startColumn, s.startLineNumber, word.endColumn); + currentMatch = new Selection(s.startLineNumber, word.startColumn, s.startLineNumber, word.endColumn); } else { searchText = editor.getModel().getValueInRange(s); } @@ -392,7 +392,7 @@ function multiCursorFind(editor:editorCommon.ICommonCodeEditor, changeFindSearch searchText: searchText, matchCase: matchCase, wholeWord: wholeWord, - nextMatch: nextMatch + currentMatch: currentMatch }; } @@ -406,8 +406,8 @@ export class SelectNextFindMatchAction extends EditorAction { if (!r) { return null; } - if (r.nextMatch) { - return r.nextMatch; + if (r.currentMatch) { + return r.currentMatch; } let allSelections = this.editor.getSelections(); @@ -423,6 +423,33 @@ export class SelectNextFindMatchAction extends EditorAction { } } +export class SelectPreviousFindMatchAction extends EditorAction { + constructor(descriptor:editorCommon.IEditorActionDescriptorData, editor:editorCommon.ICommonCodeEditor) { + super(descriptor, editor, Behaviour.WidgetFocus); + } + + protected _getPreviousMatch(): Selection { + let r = multiCursorFind(this.editor, true); + if (!r) { + return null; + } + if (r.currentMatch) { + return r.currentMatch; + } + + let allSelections = this.editor.getSelections(); + let lastAddedSelection = allSelections[allSelections.length - 1]; + + let previousMatch = this.editor.getModel().findPreviousMatch(r.searchText, lastAddedSelection.getStartPosition(), false, r.matchCase, r.wholeWord); + + if (!previousMatch) { + return null; + } + + return new Selection(previousMatch.startLineNumber, previousMatch.startColumn, previousMatch.endLineNumber, previousMatch.endColumn); + } +} + export class AddSelectionToNextFindMatchAction extends SelectNextFindMatchAction { static ID = FIND_IDS.AddSelectionToNextFindMatchAction; @@ -445,6 +472,28 @@ export class AddSelectionToNextFindMatchAction extends SelectNextFindMatchAction } } +export class AddSelectionToPreviousFindMatchAction extends SelectPreviousFindMatchAction { + static ID = FIND_IDS.AddSelectionToPreviousFindMatchAction; + + constructor(descriptor:editorCommon.IEditorActionDescriptorData, editor:editorCommon.ICommonCodeEditor) { + super(descriptor, editor); + } + + public run(): TPromise { + let previousMatch = this._getPreviousMatch(); + + if (!previousMatch) { + return TPromise.as(false); + } + + let allSelections = this.editor.getSelections(); + this.editor.setSelections(allSelections.concat(previousMatch)); + this.editor.revealRangeInCenterIfOutsideViewport(previousMatch); + + return TPromise.as(true); + } +} + export class MoveSelectionToNextFindMatchAction extends SelectNextFindMatchAction { static ID = FIND_IDS.MoveSelectionToNextFindMatchAction; @@ -467,6 +516,28 @@ export class MoveSelectionToNextFindMatchAction extends SelectNextFindMatchActio } } +export class MoveSelectionToPreviousFindMatchAction extends SelectPreviousFindMatchAction { + static ID = FIND_IDS.MoveSelectionToPreviousFindMatchAction; + + constructor(descriptor:editorCommon.IEditorActionDescriptorData, editor:editorCommon.ICommonCodeEditor) { + super(descriptor, editor); + } + + public run(): TPromise { + let previousMatch = this._getPreviousMatch(); + + if (!previousMatch) { + return TPromise.as(false); + } + + let allSelections = this.editor.getSelections(); + this.editor.setSelections(allSelections.slice(0, allSelections.length - 1).concat(previousMatch)); + this.editor.revealRangeInCenterIfOutsideViewport(previousMatch); + + return TPromise.as(true); + } +} + export class SelectHighlightsAction extends EditorAction { static ID = 'editor.action.selectHighlights'; static COMPAT_ID = 'editor.action.changeAll'; @@ -566,7 +637,7 @@ export class SelectionHighlighter extends Disposable implements editorCommon.IEd } let hasFindOccurences = DocumentHighlightProviderRegistry.has(model); - if (r.nextMatch) { + if (r.currentMatch) { // This is an empty selection if (hasFindOccurences) { // Do not interfere with semantic word highlighting in the no selection case @@ -574,7 +645,7 @@ export class SelectionHighlighter extends Disposable implements editorCommon.IEd return; } - this.lastWordUnderCursor = r.nextMatch; + this.lastWordUnderCursor = r.currentMatch; } if (/^[ \t]+$/.test(r.searchText)) { // whitespace only selection @@ -697,10 +768,19 @@ CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(MoveSelecti context: ContextKey.EditorFocus, primary: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_D) }, 'Move Last Selection To Next Find Match')); +CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(MoveSelectionToPreviousFindMatchAction, MoveSelectionToPreviousFindMatchAction.ID, nls.localize('moveSelectionToPreviousFindMatch', "Move Last Selection To Previous Find Match"), { + context: ContextKey.EditorFocus, + primary: 0 +}, 'Move Last Selection To Previous Find Match')); + CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(AddSelectionToNextFindMatchAction, AddSelectionToNextFindMatchAction.ID, nls.localize('addSelectionToNextFindMatch', "Add Selection To Next Find Match"), { context: ContextKey.EditorFocus, primary: KeyMod.CtrlCmd | KeyCode.KEY_D }, 'Add Selection To Next Find Match')); +CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(AddSelectionToPreviousFindMatchAction, AddSelectionToPreviousFindMatchAction.ID, nls.localize('addSelectionToPreviousFindMatch', "Add Selection To Previous Find Match"), { + context: ContextKey.EditorFocus, + primary: 0 +}, 'Add Selection To Previous Find Match')); function registerFindCommand(id:string, callback:(controller:CommonFindController)=>void, keybindings:IKeybindings, needsKey:string = null): void { CommonEditorRegistry.registerEditorCommand(id, CommonEditorRegistry.commandWeight(5), keybindings, false, needsKey, (ctx, editor, args) => { diff --git a/src/vs/editor/contrib/find/common/findDecorations.ts b/src/vs/editor/contrib/find/common/findDecorations.ts index 1648cb53643..364ea1f3c56 100644 --- a/src/vs/editor/contrib/find/common/findDecorations.ts +++ b/src/vs/editor/contrib/find/common/findDecorations.ts @@ -14,6 +14,7 @@ export class FindDecorations implements IDisposable { private _editor:editorCommon.ICommonCodeEditor; private _decorations:string[]; private _findScopeDecorationId:string; + private _lineHighlightDecorationId:string; private _highlightedDecorationId:string; private _startPosition:Position; @@ -21,6 +22,7 @@ export class FindDecorations implements IDisposable { this._editor = editor; this._decorations = []; this._findScopeDecorationId = null; + this._lineHighlightDecorationId = null; this._highlightedDecorationId = null; this._startPosition = this._editor.getPosition(); } @@ -31,6 +33,7 @@ export class FindDecorations implements IDisposable { this._editor = null; this._decorations = []; this._findScopeDecorationId = null; + this._lineHighlightDecorationId = null; this._highlightedDecorationId = null; this._startPosition = null; } @@ -38,6 +41,7 @@ export class FindDecorations implements IDisposable { public reset(): void { this._decorations = []; this._findScopeDecorationId = null; + this._lineHighlightDecorationId = null; this._highlightedDecorationId = null; } @@ -95,6 +99,14 @@ export class FindDecorations implements IDisposable { this._highlightedDecorationId = newCurrentDecorationId; changeAccessor.changeDecorationOptions(this._highlightedDecorationId, FindDecorations.createFindMatchDecorationOptions(true)); } + if (this._lineHighlightDecorationId !== null) { + changeAccessor.removeDecoration(this._lineHighlightDecorationId); + this._lineHighlightDecorationId = null; + } + if (newCurrentDecorationId !== null) { + let rng = this._editor.getModel().getDecorationRange(newCurrentDecorationId); + this._lineHighlightDecorationId = changeAccessor.addDecoration(rng, FindDecorations.createLineHighlightDecoration()); + } }); } @@ -122,6 +134,7 @@ export class FindDecorations implements IDisposable { this._findScopeDecorationId = null; } this._decorations = tmpDecorations; + this._lineHighlightDecorationId = null; this._highlightedDecorationId = null; } @@ -131,6 +144,9 @@ export class FindDecorations implements IDisposable { if (this._findScopeDecorationId) { result.push(this._findScopeDecorationId); } + if (this._lineHighlightDecorationId) { + result.push(this._lineHighlightDecorationId); + } return result; } @@ -146,6 +162,14 @@ export class FindDecorations implements IDisposable { }; } + private static createLineHighlightDecoration(): editorCommon.IModelDecorationOptions { + return { + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'lineHighlight', + isWholeLine: true + }; + } + private static createFindScopeDecorationOptions(): editorCommon.IModelDecorationOptions { return { className: 'findScope', diff --git a/src/vs/editor/contrib/find/common/findModel.ts b/src/vs/editor/contrib/find/common/findModel.ts index 0ad450ab36a..78fc3423b5c 100644 --- a/src/vs/editor/contrib/find/common/findModel.ts +++ b/src/vs/editor/contrib/find/common/findModel.ts @@ -23,7 +23,9 @@ export const FIND_IDS = { NextSelectionMatchFindAction: 'editor.action.nextSelectionMatchFindAction', PreviousSelectionMatchFindAction: 'editor.action.previousSelectionMatchFindAction', AddSelectionToNextFindMatchAction: 'editor.action.addSelectionToNextFindMatch', + AddSelectionToPreviousFindMatchAction: 'editor.action.addSelectionToPreviousFindMatch', MoveSelectionToNextFindMatchAction: 'editor.action.moveSelectionToNextFindMatch', + MoveSelectionToPreviousFindMatchAction: 'editor.action.moveSelectionToPreviousFindMatch', StartFindReplaceAction: 'editor.action.startFindReplaceAction', CloseFindWidgetCommand: 'closeFindWidget', ToggleCaseSensitiveCommand: 'toggleFindCaseSensitive', diff --git a/src/vs/editor/contrib/outlineMarker/browser/outlineMarker.css b/src/vs/editor/contrib/outlineMarker/browser/outlineMarker.css deleted file mode 100644 index 28e72cce7c5..00000000000 --- a/src/vs/editor/contrib/outlineMarker/browser/outlineMarker.css +++ /dev/null @@ -1,17 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -hr.outlineRule.class { - border: 0; - border-bottom: 2px solid gray; - width: 98%; - margin: 0; -} - -hr.outlineRule.method, hr.outlineRule.function { - border: 0; - border-bottom: 1px dotted gray; - width: 98%; - margin: 0; -} \ No newline at end of file diff --git a/src/vs/editor/contrib/outlineMarker/browser/outlineMarker.ts b/src/vs/editor/contrib/outlineMarker/browser/outlineMarker.ts deleted file mode 100644 index 2360502202b..00000000000 --- a/src/vs/editor/contrib/outlineMarker/browser/outlineMarker.ts +++ /dev/null @@ -1,271 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import 'vs/css!./outlineMarker'; -import {RunOnceScheduler} from 'vs/base/common/async'; -import {onUnexpectedError} from 'vs/base/common/errors'; -import {IDisposable, dispose} from 'vs/base/common/lifecycle'; -import {TPromise} from 'vs/base/common/winjs.base'; -import {Range} from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import {SymbolInformation, SymbolKind, DocumentSymbolProviderRegistry} from 'vs/editor/common/modes'; -import {ICodeEditor, IViewZone, IViewZoneChangeAccessor} from 'vs/editor/browser/editorBrowser'; -import {EditorBrowserRegistry} from 'vs/editor/browser/editorBrowserExtensions'; -import {getDocumentSymbols, IOutline} from 'vs/editor/contrib/quickOpen/common/quickOpen'; - -class OutlineViewZone implements IViewZone { - - public afterLineNumber:number; - public heightInPx:number; - public suppressMouseDown:boolean; - - public domNode:HTMLElement; - - constructor(range:editorCommon.IRange, outlineType:SymbolKind) { - this.afterLineNumber = range.startLineNumber-1; - this.heightInPx = 4; - this.suppressMouseDown = true; - - this.domNode = document.createElement('div'); - var hr = document.createElement('hr'); - hr.className = 'outlineRule ' + SymbolKind.from(outlineType); - this.domNode.appendChild(hr); - } -} - -interface IDecorationIdCallback { - (decorationId:string):void; -} - -class OutlineMarkerHelper { - private _removeDecorations:string[]; - private _addDecorations:editorCommon.IModelDeltaDecoration[]; - private _addDecorationsCallbacks:IDecorationIdCallback[]; - - constructor() { - this._removeDecorations = []; - this._addDecorations = []; - this._addDecorationsCallbacks = []; - } - - public addDecoration(decoration:editorCommon.IModelDeltaDecoration, callback:IDecorationIdCallback): void { - this._addDecorations.push(decoration); - this._addDecorationsCallbacks.push(callback); - } - - public removeDecoration(decorationId:string): void { - this._removeDecorations.push(decorationId); - } - - public commit(changeAccessor:editorCommon.IModelDecorationsChangeAccessor): void { - var resultingDecorations = changeAccessor.deltaDecorations(this._removeDecorations, this._addDecorations); - for (let i = 0, len = resultingDecorations.length; i < len; i++) { - this._addDecorationsCallbacks[i](resultingDecorations[i]); - } - } -} - -class OutlineMarker { - private _viewZone:OutlineViewZone; - private _viewZoneId:number; - private _decorationId:string; - - private _editor:ICodeEditor; - - - public constructor(range:editorCommon.IRange, outlineType:SymbolKind, _editor:ICodeEditor, helper:OutlineMarkerHelper, viewZoneChangeAccessor:IViewZoneChangeAccessor) { - this._editor = _editor; - this._viewZone = new OutlineViewZone(range, outlineType); - this._viewZoneId = viewZoneChangeAccessor.addZone(this._viewZone); - helper.addDecoration({ - range: range, - options: {} - }, (decorationId) => { - this._decorationId = decorationId; - }); - } - - public dispose(helper:OutlineMarkerHelper, viewZoneChangeAccessor:IViewZoneChangeAccessor): void { - helper.removeDecoration(this._decorationId); - viewZoneChangeAccessor.removeZone(this._viewZoneId); - } - - public getLine(): number { - return this._viewZone.afterLineNumber; - } - - public update(viewZoneChangeAccessor:IViewZoneChangeAccessor): void { - var range = this._editor.getModel().getDecorationRange(this._decorationId); - - this._viewZone.afterLineNumber = range.startLineNumber - 1; - viewZoneChangeAccessor.layoutZone(this._viewZoneId); - } -} - -export class OutlineMarkerContribution implements editorCommon.IEditorContribution { - - public static ID = 'editor.outlineMarker'; - - private _editor:ICodeEditor; - private _isEnabled: boolean; - - private _globalToDispose:IDisposable[]; - - private _localToDispose:IDisposable[]; - private _currentOutlinePromise:TPromise; - - private _markers:OutlineMarker[]; - - constructor(editor:ICodeEditor) { - this._editor = editor; - this._isEnabled = this._editor.getConfiguration().contribInfo.outlineMarkers; - - this._globalToDispose = []; - this._localToDispose = []; - this._markers = []; - this._currentOutlinePromise = null; - - this._globalToDispose.push(this._editor.onDidChangeModel(() => this.onChange(true))); - this._globalToDispose.push(this._editor.onDidChangeModelMode(() => this.onChange(false))); - this._globalToDispose.push(this._editor.onDidChangeConfiguration((e: editorCommon.IConfigurationChangedEvent) => { - let oldIsEnabled = this._isEnabled; - this._isEnabled = this._editor.getConfiguration().contribInfo.outlineMarkers; - if (oldIsEnabled !== this._isEnabled) { - this.onChange(false); - } - })); - - this.onChange(false); - } - - public dispose(): void { - this.localDispose(); - this._globalToDispose = dispose(this._globalToDispose); - } - - private localDispose(): void { - if (this._currentOutlinePromise) { - this._currentOutlinePromise.cancel(); - } - this._localToDispose = dispose(this._localToDispose); - } - - public getId():string { - return OutlineMarkerContribution.ID; - } - - private onChange( markersAlreadyDisposed : boolean): void { - - if (markersAlreadyDisposed) { - this._markers = []; - } - - this.localDispose(); - - if (!this._isEnabled) { - return; - } - - var model = this._editor.getModel(); - if (!model) { - return; - } - - if (!DocumentSymbolProviderRegistry.has(model)) { - return; - } - - var scheduler = new RunOnceScheduler(() => { - if (this._currentOutlinePromise) { - this._currentOutlinePromise.cancel(); - } - - this._currentOutlinePromise = getDocumentSymbols(model); - - this._currentOutlinePromise.then((result) => { - this.renderOutlines(result.entries); - }, (error) => { - onUnexpectedError(error); - }); - }, 250); - this._localToDispose.push(scheduler); - this._localToDispose.push(this._editor.onDidChangeModelContent(() => { - - // Synchronously move markers - this._editor.changeViewZones((viewAccessor) => { - this._markers.forEach((marker) => { - marker.update(viewAccessor); - }); - }); - - scheduler.schedule(); - })); - this._localToDispose.push({ - dispose: () => { - if (this._markers.length > 0) { - var helper = new OutlineMarkerHelper(); - this._editor.changeViewZones((accessor) => { - this._markers.forEach((marker) => marker.dispose(helper, accessor)); - this._markers = []; - }); - this._editor.changeDecorations((accessor) => { - helper.commit(accessor); - }); - } - } - }); - - scheduler.schedule(); - } - - private renderOutlines(entries: SymbolInformation[]): void { - var centeredRange = this._editor.getCenteredRangeInViewport(); - var oldMarkersCount = this._markers.length; - this._editor.changeDecorations((decorationsAccessor) => { - var helper = new OutlineMarkerHelper(); - this._editor.changeViewZones((viewzonesAccessor) => { - this._markers.forEach((marker) => marker.dispose(helper, viewzonesAccessor)); - this._markers = []; - - this.renderOutlinesRecursive(entries, helper, viewzonesAccessor); - }); - helper.commit(decorationsAccessor); - }); - var newMarkersCount = this._markers.length; - if (Math.abs(oldMarkersCount - newMarkersCount) > 1) { - // Reveal only if the delta is more than 1 marker - this._editor.revealRangeInCenter(centeredRange); - } - } - - private renderOutlinesRecursive(entries: SymbolInformation[], helper:OutlineMarkerHelper, viewZoneChangeAccessor:IViewZoneChangeAccessor): void { - if (entries) { - entries.forEach((outline) => { - if (outline.kind === SymbolKind.Class || outline.kind === SymbolKind.Method || outline.kind === SymbolKind.Function) { - var range = Range.lift(outline.location.range); - if (!this.alreadyHasMarkerAtRange(range)) { - var marker = new OutlineMarker(range, outline.kind, this._editor, helper, viewZoneChangeAccessor); - this._markers.push(marker); - } - } - }); - } - } - - private alreadyHasMarkerAtRange(range: editorCommon.IRange): boolean { - for (var i = 0; i < this._markers.length; ++i) { - if (this._markers[i].getLine() === range.startLineNumber-1) { - return true; - } - } - return false; - } - -} - - -EditorBrowserRegistry.registerEditorContribution(OutlineMarkerContribution); \ No newline at end of file diff --git a/src/vs/editor/contrib/rename/browser/renameModel.ts b/src/vs/editor/contrib/rename/browser/renameModel.ts deleted file mode 100644 index 3db1e5ba69d..00000000000 --- a/src/vs/editor/contrib/rename/browser/renameModel.ts +++ /dev/null @@ -1,208 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 * as nls from 'vs/nls'; -import * as arrays from 'vs/base/common/arrays'; -import * as collections from 'vs/base/common/collections'; -import URI from 'vs/base/common/uri'; -import {TPromise} from 'vs/base/common/winjs.base'; -import {IEditorService} from 'vs/platform/editor/common/editor'; -import {EditOperation} from 'vs/editor/common/core/editOperation'; -import {Range} from 'vs/editor/common/core/range'; -import {Selection} from 'vs/editor/common/core/selection'; -import {IIdentifiedSingleEditOperation, IModel, IRange} from 'vs/editor/common/editorCommon'; -import {IResourceEdit} from 'vs/editor/common/modes'; - -class EditTask { - private _initialSelections: Selection[]; - private _endCursorSelection: Selection; - private _model: IModel; - private _edits: IIdentifiedSingleEditOperation[]; - - constructor(model: IModel) { - this._endCursorSelection = null; - this._model = model; - this._edits = []; - } - - public addEdit(edit: IResourceEdit): void { - var range: IRange; - if (!edit.range) { - range = this._model.getFullModelRange(); - } else { - range = edit.range; - } - this._edits.push(EditOperation.replace(Range.lift(range), edit.newText)); - } - - public apply(): void { - if (this._edits.length === 0) { - return; - } - this._edits.sort(EditTask._editCompare); - - this._initialSelections = this._getInitialSelections(); - this._model.pushEditOperations(this._initialSelections, this._edits, (edits) => this._getEndCursorSelections(edits)); - } - - protected _getInitialSelections(): Selection[] { - var firstRange = this._edits[0].range; - var initialSelection = new Selection( - firstRange.startLineNumber, - firstRange.startColumn, - firstRange.endLineNumber, - firstRange.endColumn - ); - return [initialSelection]; - } - - private _getEndCursorSelections(inverseEditOperations:IIdentifiedSingleEditOperation[]): Selection[] { - var relevantEditIndex = 0; - for (var i = 0; i < inverseEditOperations.length; i++) { - var editRange = inverseEditOperations[i].range; - for (var j = 0; j < this._initialSelections.length; j++) { - var selectionRange = this._initialSelections[j]; - if (Range.areIntersectingOrTouching(editRange, selectionRange)) { - relevantEditIndex = i; - break; - } - } - } - - var srcRange = inverseEditOperations[relevantEditIndex].range; - this._endCursorSelection = new Selection( - srcRange.endLineNumber, - srcRange.endColumn, - srcRange.endLineNumber, - srcRange.endColumn - ); - return [this._endCursorSelection]; - } - - public getEndCursorSelection(): Selection { - return this._endCursorSelection; - } - - private static _editCompare(a: IIdentifiedSingleEditOperation, b: IIdentifiedSingleEditOperation): number { - return Range.compareRangesUsingStarts(a.range, b.range); - } -} - -class SourceModelEditTask extends EditTask { - - private _knownInitialSelections:Selection[]; - - constructor(model: IModel, initialSelections:Selection[]) { - super(model); - this._knownInitialSelections = initialSelections; - } - - protected _getInitialSelections(): Selection[] { - return this._knownInitialSelections; - } -} - -export default class RenameModel { - - private _editorService: IEditorService; - private _numberOfResourcesToModify: number = 0; - private _numberOfChanges: number = 0; - private _edits: collections.IStringDictionary = Object.create(null); - private _rejectReasons: string[]; - private _tasks: EditTask[]; - private _sourceModel: URI; - private _sourceSelections: Selection[]; - private _sourceModelTask: SourceModelEditTask; - - constructor(editorService: IEditorService, sourceModel: URI, sourceSelections: Selection[], editsOrReject: string|IResourceEdit[]) { - this._editorService = editorService; - this._sourceModel = sourceModel; - this._sourceSelections = sourceSelections; - this._sourceModelTask = null; - - if (typeof editsOrReject === 'string') { - this.reject(editsOrReject); - } else { - editsOrReject.forEach(this._addEdit, this); - } - } - - public reject(value: string): void { - if (!this._rejectReasons) { - this._rejectReasons = []; - } - this._rejectReasons.push(value); - } - - public isRejected(): boolean { - return !arrays.isFalsyOrEmpty(this._rejectReasons); - } - - public rejectReasons(): string[]{ - return this._rejectReasons; - } - - public resourcesCount(): number { - return this._numberOfResourcesToModify; - } - - public changeCount(): number { - return this._numberOfChanges; - } - - private _addEdit(edit: IResourceEdit): void { - var array = this._edits[edit.resource.toString()]; - if (!array) { - this._edits[edit.resource.toString()] = array = []; - this._numberOfResourcesToModify += 1; - } - this._numberOfChanges += 1; - array.push(edit); - } - - public prepare(): TPromise { - - if (this._tasks) { - throw new Error('illegal state - already prepared'); - } - - this._tasks = []; - var promises: TPromise[] = []; - - collections.forEach(this._edits, entry => { - var promise = this._editorService.resolveEditorModel({ resource: URI.parse(entry.key) }).then(model => { - if (!model || !model.textEditorModel) { - this.reject(nls.localize('cannotLoadFile', "Cannot load file {0}", entry.key)); - } else { - var textEditorModel = model.textEditorModel, - task: EditTask; - - if (textEditorModel.uri.toString() === this._sourceModel.toString()) { - this._sourceModelTask = new SourceModelEditTask(textEditorModel, this._sourceSelections); - task = this._sourceModelTask; - } else { - task = new EditTask(textEditorModel); - } - - entry.value.forEach(edit => task.addEdit(edit)); - this._tasks.push(task); - } - }); - promises.push(promise); - }); - - return TPromise.join(promises).then(_ => this); - } - - public apply(): Selection { - this._tasks.forEach(task => task.apply()); - var r: Selection = null; - if (this._sourceModelTask) { - r = this._sourceModelTask.getEndCursorSelection(); - } - return r; - } -} diff --git a/src/vs/editor/contrib/suggest/browser/completionModel.ts b/src/vs/editor/contrib/suggest/browser/completionModel.ts index 809e9a24bf6..9382d9256a6 100644 --- a/src/vs/editor/contrib/suggest/browser/completionModel.ts +++ b/src/vs/editor/contrib/suggest/browser/completionModel.ts @@ -124,6 +124,11 @@ export class CompletionModel { item.highlights = filter(word, suggestion.label); match = item.highlights !== null; + // no match on label -> check on codeSnippet + if (!match && suggestion.codeSnippet !== suggestion.label) { + match = !isFalsyOrEmpty((filter(word, suggestion.codeSnippet.replace(/{{.+?}}/g, '')))); // filters {{text}}-snippet syntax + } + // no match on label nor codeSnippet -> check on filterText if(!match && typeof suggestion.filterText === 'string') { match = !isFalsyOrEmpty(filter(word, suggestion.filterText)); diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index 1ec4ae1e8b2..4a77c2b222f 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -8,7 +8,6 @@ import 'vs/css!./suggest'; import * as nls from 'vs/nls'; import * as strings from 'vs/base/common/strings'; -import * as timer from 'vs/base/common/timer'; import { TPromise } from 'vs/base/common/winjs.base'; import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; @@ -19,7 +18,6 @@ import { List } from 'vs/base/browser/ui/list/listWidget'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingContextKey, IKeybindingService } from 'vs/platform/keybinding/common/keybindingService'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationChangedEvent } from 'vs/editor/common/editorCommon'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { Context as SuggestContext } from '../common/suggest'; @@ -179,15 +177,6 @@ function computeScore(suggestion: string, currentWord: string, currentWordLowerC return score; } -interface ITelemetryData { - suggestionCount?: number; - suggestedIndex?: number; - selectedIndex?: number; - hintLength?: number; - wasCancelled?: boolean; - wasAutomaticallyTriggered?: boolean; -} - enum State { Hidden, Loading, @@ -321,10 +310,6 @@ export class SuggestWidget implements IContentWidget, IDisposable { private focusedItem: CompletionItem; private completionModel: CompletionModel; - private telemetryData: ITelemetryData; - private telemetryService: ITelemetryService; - private telemetryTimer: timer.ITimerEvent; - private element: HTMLElement; private messageElement: HTMLElement; private listElement: HTMLElement; @@ -344,15 +329,11 @@ export class SuggestWidget implements IContentWidget, IDisposable { private editor: ICodeEditor, private model: SuggestModel, @IKeybindingService keybindingService: IKeybindingService, - @ITelemetryService telemetryService: ITelemetryService, @IInstantiationService instantiationService: IInstantiationService ) { this.isAuto = false; this.focusedItem = null; - this.telemetryData = null; - this.telemetryService = telemetryService; - this.element = $('.editor-widget.suggest-widget.monaco-editor-background'); this.element.style.width = SuggestWidget.WIDTH + 'px'; this.element.style.top = '0'; @@ -426,11 +407,6 @@ export class SuggestWidget implements IContentWidget, IDisposable { return; } - this.telemetryData.selectedIndex = 0; - this.telemetryData.wasCancelled = false; - this.telemetryData.selectedIndex = e.indexes[0]; - this.submitTelemetryData(); - const item = e.elements[0]; const container = item.container; const overwriteBefore = (typeof item.suggestion.overwriteBefore === 'undefined') ? container.currentWord.length : item.suggestion.overwriteBefore; @@ -570,7 +546,6 @@ export class SuggestWidget implements IContentWidget, IDisposable { return; } - this.telemetryTimer = this.telemetryService.timedPublicLog('suggestWidgetLoadingTime'); this.isAuto = !!e.auto; if (!this.isAuto) { @@ -579,12 +554,6 @@ export class SuggestWidget implements IContentWidget, IDisposable { this.setState(State.Loading); }, 50); } - - if (!e.retrigger) { - this.telemetryData = { - wasAutomaticallyTriggered: e.characterTriggered - }; - } } private onDidSuggest(e: ISuggestEvent): void { @@ -629,23 +598,12 @@ export class SuggestWidget implements IContentWidget, IDisposable { } }); - this.telemetryData = this.telemetryData || {}; - this.telemetryData.suggestionCount = this.completionModel.items.length; - this.telemetryData.suggestedIndex = bestSuggestionIndex; - this.telemetryData.hintLength = currentWord.length; - this.list.splice(0, this.list.length, ...this.completionModel.items); this.list.setFocus(bestSuggestionIndex); this.list.reveal(bestSuggestionIndex, 0); this.setState(State.Open); } - - if (this.telemetryTimer) { - this.telemetryTimer.data = { reason: isEmpty ? 'empty' : 'results' }; - this.telemetryTimer.stop(); - this.telemetryTimer = null; - } } private onDidCancel(e: ICancelEvent) { @@ -656,18 +614,6 @@ export class SuggestWidget implements IContentWidget, IDisposable { if (!e.retrigger) { this.setState(State.Hidden); - - if (this.telemetryData) { - this.telemetryData.selectedIndex = -1; - this.telemetryData.wasCancelled = true; - this.submitTelemetryData(); - } - } - - if (this.telemetryTimer) { - this.telemetryTimer.data = { reason: 'cancel' }; - this.telemetryTimer.stop(); - this.telemetryTimer = null; } } @@ -812,11 +758,6 @@ export class SuggestWidget implements IContentWidget, IDisposable { return SuggestWidget.ID; } - private submitTelemetryData(): void { - this.telemetryService.publicLog('suggestWidget', this.telemetryData); - this.telemetryData = null; - } - private updateWidgetHeight(): number { let height = 0; @@ -853,9 +794,6 @@ export class SuggestWidget implements IContentWidget, IDisposable { this.suggestionSupportsAutoAccept = null; this.currentSuggestionDetails = null; this.focusedItem = null; - this.telemetryData = null; - this.telemetryService = null; - this.telemetryTimer = null; this.element = null; this.messageElement = null; this.listElement = null; diff --git a/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.ts b/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.ts index 2fb536209b4..6e0aa974e2b 100644 --- a/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.ts +++ b/src/vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode.ts @@ -10,7 +10,9 @@ import {TPromise} from 'vs/base/common/winjs.base'; import {EditorAction} from 'vs/editor/common/editorAction'; import {Behaviour} from 'vs/editor/common/editorActionEnablement'; import {ICommonCodeEditor, IEditorActionDescriptorData} from 'vs/editor/common/editorCommon'; -import {CommonEditorRegistry, ContextKey, EditorActionDescriptor} from 'vs/editor/common/editorCommonExtensions'; +import {CommonEditorRegistry, EditorActionDescriptor} from 'vs/editor/common/editorCommonExtensions'; +import {TabFocus} from 'vs/editor/common/config/commonEditorConfig'; +import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry'; export class ToggleTabFocusModeAction extends EditorAction { @@ -22,11 +24,8 @@ export class ToggleTabFocusModeAction extends EditorAction { public run():TPromise { - if(this.editor.getConfiguration().tabFocusMode) { - this.editor.updateOptions({tabFocusMode: false}); - } else { - this.editor.updateOptions({tabFocusMode: true}); - } + let oldValue = TabFocus.getTabFocusMode(); + TabFocus.setTabFocusMode(!oldValue); return TPromise.as(true); } @@ -34,7 +33,14 @@ export class ToggleTabFocusModeAction extends EditorAction { // register actions CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(ToggleTabFocusModeAction, ToggleTabFocusModeAction.ID, nls.localize('toggle.tabfocusmode', "Toggle Use of Tab Key for Setting Focus"), { - context: ContextKey.EditorTextFocus, + primary: null, + context: null +}, 'Toggle Use of Tab Key for Setting Focus')); + +KeybindingsRegistry.registerCommandRule({ + id: ToggleTabFocusModeAction.ID, + weight: KeybindingsRegistry.WEIGHT.editorContrib(), + when: null, primary: KeyMod.CtrlCmd | KeyCode.KEY_M, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_M } -}, 'Toggle Use of Tab Key for Setting Focus')); +}); diff --git a/src/vs/editor/test/common/controller/cursor.test.ts b/src/vs/editor/test/common/controller/cursor.test.ts index c927178790c..22c7d655d17 100644 --- a/src/vs/editor/test/common/controller/cursor.test.ts +++ b/src/vs/editor/test/common/controller/cursor.test.ts @@ -1197,6 +1197,115 @@ suite('Editor Controller - Regression tests', () => { }); }); + test('bug #2938 (1): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => { + usingCursor({ + text: [ + '\tfunction baz() {', + '\t\tfunction hello() { // something here', + '\t\t', + '\t', + '\t\t}', + '\t}' + ], + modelOpts: { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: true + }, + mode: new OnEnterMode(IndentAction.Indent), + }, (model, cursor) => { + moveTo(cursor, 4, 2, false); + cursorEqual(cursor, 4, 2, 4, 2); + + cursorCommand(cursor, H.Tab, null, 'keyboard'); + assert.equal(model.getLineContent(4), '\t\t\t'); + }); + }); + + + test('bug #2938 (2): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => { + usingCursor({ + text: [ + '\tfunction baz() {', + '\t\tfunction hello() { // something here', + '\t\t', + ' ', + '\t\t}', + '\t}' + ], + modelOpts: { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: true + }, + mode: new OnEnterMode(IndentAction.Indent), + }, (model, cursor) => { + moveTo(cursor, 4, 1, false); + cursorEqual(cursor, 4, 1, 4, 1); + + cursorCommand(cursor, H.Tab, null, 'keyboard'); + assert.equal(model.getLineContent(4), '\t\t\t'); + }); + }); + + test('bug #2938 (3): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => { + usingCursor({ + text: [ + '\tfunction baz() {', + '\t\tfunction hello() { // something here', + '\t\t', + '\t\t\t', + '\t\t}', + '\t}' + ], + modelOpts: { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: true + }, + mode: new OnEnterMode(IndentAction.Indent), + }, (model, cursor) => { + moveTo(cursor, 4, 3, false); + cursorEqual(cursor, 4, 3, 4, 3); + + cursorCommand(cursor, H.Tab, null, 'keyboard'); + assert.equal(model.getLineContent(4), '\t\t\t\t'); + }); + }); + + test('bug #2938 (4): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => { + usingCursor({ + text: [ + '\tfunction baz() {', + '\t\tfunction hello() { // something here', + '\t\t', + '\t\t\t\t', + '\t\t}', + '\t}' + ], + modelOpts: { + defaultEOL: DefaultEndOfLine.LF, + detectIndentation: false, + insertSpaces: false, + tabSize: 4, + trimAutoWhitespace: true + }, + mode: new OnEnterMode(IndentAction.Indent), + }, (model, cursor) => { + moveTo(cursor, 4, 4, false); + cursorEqual(cursor, 4, 4, 4, 4); + + cursorCommand(cursor, H.Tab, null, 'keyboard'); + assert.equal(model.getLineContent(4), '\t\t\t\t\t'); + }); + }); + test('Bug 18276:[editor] Indentation broken when selection is empty', () => { usingCursor({ text: [ diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index ad77581eec1..68cb4d472ba 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1181,13 +1181,6 @@ declare module monaco.editor { * Defaults to '.'. */ wordWrapBreakObtrusiveCharacters?: string; - /** - * Control what pressing Tab does. - * If it is false, pressing Tab or Shift-Tab will be handled by the editor. - * If it is true, pressing Tab or Shift-Tab will move the browser focus. - * Defaults to false. - */ - tabFocusMode?: boolean; /** * Performance guard: Stop rendering a line after x characters. * Defaults to 10000 if wrappingColumn is -1. Defaults to -1 if wrappingColumn is >= 0. @@ -1253,11 +1246,6 @@ declare module monaco.editor { * Defaults to true. */ selectionHighlight?: boolean; - /** - * Show lines before classes and methods (based on outline info). - * Defaults to false. - */ - outlineMarkers?: boolean; /** * Show reference infos (a.k.a. code lenses) for modes that support it * Defaults to true. @@ -1416,7 +1404,6 @@ declare module monaco.editor { suggestOnTriggerCharacters: boolean; acceptSuggestionOnEnter: boolean; selectionHighlight: boolean; - outlineMarkers: boolean; referenceInfos: boolean; folding: boolean; } @@ -2744,6 +2731,11 @@ declare module monaco.editor { */ export const KEYBINDING_CONTEXT_EDITOR_FOCUS: string; + /** + * A context key that is set when the editor's text is readonly. + */ + export const KEYBINDING_CONTEXT_EDITOR_READONLY: string; + /** * A context key that is set when the editor has multiple selections (multiple cursors). */ @@ -3909,8 +3901,8 @@ declare module monaco.languages { } /** - * The language configuration interfaces defines the contract between extensions - * and various editor features, like automatic bracket insertion, automatic indentation etc. + * The language configuration interface defines the contract between extensions and + * various editor features, like automatic bracket insertion, automatic indentation etc. */ export interface LanguageConfiguration { /** diff --git a/src/vs/platform/actions/browser/menuService.ts b/src/vs/platform/actions/browser/menuService.ts index 3e08735b50b..d303fee8510 100644 --- a/src/vs/platform/actions/browser/menuService.ts +++ b/src/vs/platform/actions/browser/menuService.ts @@ -11,64 +11,43 @@ import {IDisposable, dispose} from 'vs/base/common/lifecycle'; import {IAction} from 'vs/base/common/actions'; import {values} from 'vs/base/common/collections'; import {KbExpr, IKeybindingService} from 'vs/platform/keybinding/common/keybindingService'; -import {MenuId, CommandAction, MenuItemAction, IMenu, IMenuItem, IMenuService} from 'vs/platform/actions/common/actions'; +import {MenuId, ICommandAction, MenuItemAction, IMenu, IMenuItem, IMenuService} from 'vs/platform/actions/common/actions'; import {IExtensionService} from 'vs/platform/extensions/common/extensions'; import {ResourceContextKey} from 'vs/platform/actions/common/resourceContextKey'; -export interface IDeclaredMenuItem { - command: string; - alt?: string; - when?: string; - group?: string; -} - export interface IMenuRegistry { - addCommand(userCommand: CommandAction): boolean; - hasCommand(id: string): boolean; - addMenuItems(location: MenuId, items: IDeclaredMenuItem[]): void; + addCommand(userCommand: ICommandAction): boolean; + getCommand(id: string): ICommandAction; + appendMenuItem(menu: MenuId, item: IMenuItem): void; } const _registry = new class { - commands: { [id: string]: CommandAction } = Object.create(null); + commands: { [id: string]: ICommandAction } = Object.create(null); - menuItems: { [loc: number]: IDeclaredMenuItem[] } = Object.create(null); + menuItems: { [loc: number]: IMenuItem[] } = Object.create(null); - addCommand(command: CommandAction): boolean { + addCommand(command: ICommandAction): boolean { const old = this.commands[command.id]; this.commands[command.id] = command; return old !== void 0; } - hasCommand(id: string): boolean { - return this.commands[id] !== void 0; + getCommand(id: string): ICommandAction { + return this.commands[id]; } - addMenuItems(loc: MenuId, items: IDeclaredMenuItem[]): void { + appendMenuItem(loc: MenuId, items: IMenuItem): void { let array = this.menuItems[loc]; if (!array) { - this.menuItems[loc] = items; + this.menuItems[loc] = [items]; } else { - array.push(...items); + array.push(items); } } getMenuItems(loc: MenuId): IMenuItem[] { - const result: IMenuItem[] = []; - const menuItems = this.menuItems[loc]; - if (menuItems) { - for (let item of menuItems) { - const command = this.commands[item.command]; - if (!command) { - // warn? - continue; - } - const when = KbExpr.deserialize(item.when); - const alt = this.commands[item.alt]; - result.push({ when, command, alt, group: item.group }); - } - } - return result; + return this.menuItems[loc] || []; } }; @@ -88,7 +67,7 @@ export class MenuService implements IMenuService { return new Menu(id, keybindingService, this._extensionService); } - getCommandActions(): CommandAction[] { + getCommandActions(): ICommandAction[] { return values(_registry.commands); } } @@ -181,14 +160,15 @@ class Menu implements IMenu { } private static _compareMenuItems(a: IMenuItem, b: IMenuItem): number { - let ret: number; - if (a.group && b.group) { - ret = Menu._compareGroupId(a.group, b.group); + if (a.group === b.group) { + return a.command.title.localeCompare(b.command.title); + } else if (!a.group) { + return 1; + } else if (!b.group) { + return -1; + } else { + return Menu._compareGroupId(a.group, b.group); } - if (!ret) { - ret = a.command.title.localeCompare(b.command.title); - } - return ret; } private static _compareGroupId(a: string, b: string): number { diff --git a/src/vs/platform/actions/browser/menusExtensionPoint.ts b/src/vs/platform/actions/browser/menusExtensionPoint.ts index 1c1a3986079..e808889d556 100644 --- a/src/vs/platform/actions/browser/menusExtensionPoint.ts +++ b/src/vs/platform/actions/browser/menusExtensionPoint.ts @@ -11,13 +11,21 @@ import {IdGenerator} from 'vs/base/common/idGenerator'; import {IJSONSchema} from 'vs/base/common/jsonSchema'; import {forEach} from 'vs/base/common/collections'; import {IExtensionPointUser, IExtensionMessageCollector, ExtensionsRegistry} from 'vs/platform/extensions/common/extensionsRegistry'; -import {IDeclaredMenuItem, MenuRegistry} from './menuService'; +import {MenuRegistry} from './menuService'; +import {KbExpr} from 'vs/platform/keybinding/common/keybindingService'; import {MenuId} from 'vs/platform/actions/common/actions'; namespace schema { // --- menus contribution point + export interface IUserFriendlyMenuItem { + command: string; + alt?: string; + when?: string; + group?: string; + } + export function parseMenuId(value: string): MenuId { switch (value) { case 'editor/title': return MenuId.EditorTitle; @@ -26,7 +34,7 @@ namespace schema { } } - export function isValidMenuItems(menu: IDeclaredMenuItem[], collector: IExtensionMessageCollector): boolean { + export function isValidMenuItems(menu: IUserFriendlyMenuItem[], collector: IExtensionMessageCollector): boolean { if (!Array.isArray(menu)) { collector.error(localize('requirearry', "menu items must be an arry")); return false; @@ -237,7 +245,7 @@ ExtensionsRegistry.registerExtensionPoint('menus', schema.menusContribtion).setHandler(extensions => { +ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyMenuItem[] }>('menus', schema.menusContribtion).setHandler(extensions => { for (let extension of extensions) { const {value, collector} = extension; @@ -253,10 +261,13 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: IDeclaredMenuItem[] } } for (let item of entry.value) { - if (!MenuRegistry.hasCommand(item.command)) { + let command = MenuRegistry.getCommand(item.command); + let alt = item.alt && MenuRegistry.getCommand(item.alt); + + if (!command) { collector.warn(localize('missing.command', "Menu item references a command `{0}` which is not defined in the 'commands' section.", item.command)); } - if (item.alt && !MenuRegistry.hasCommand(item.alt)) { + if (item.alt && !alt) { collector.warn(localize('missing.altCommand', "Menu item references an alt-command `{0}` which is not defined in the 'commands' section.", item.alt)); } if (item.command === item.alt) { @@ -266,9 +277,14 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: IDeclaredMenuItem[] } if (item.alt && menu !== MenuId.EditorTitle && item.group !== 'navigation') { collector.info(localize('nosupport.altCommand', "Sorry, but currently only the 'navigation' group of the 'editor/title' menu supports alt-commands")); } - } - MenuRegistry.addMenuItems(menu, entry.value); + MenuRegistry.appendMenuItem(menu, { + command, + alt, + group: item.group || undefined, + when: KbExpr.deserialize(item.when) + }); + } }); } }); diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 4879ce0559f..3f0eff82a56 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -7,7 +7,6 @@ import URI from 'vs/base/common/uri'; import Actions = require('vs/base/common/actions'); import WinJS = require('vs/base/common/winjs.base'); -import Assert = require('vs/base/common/assert'); import Descriptors = require('vs/platform/instantiation/common/descriptors'); import Instantiation = require('vs/platform/instantiation/common/instantiation'); import {KbExpr, IKeybindings, IKeybindingService} from 'vs/platform/keybinding/common/keybindingService'; @@ -15,7 +14,7 @@ import {IDisposable} from 'vs/base/common/lifecycle'; import {createDecorator} from 'vs/platform/instantiation/common/instantiation'; import Event from 'vs/base/common/event'; -export interface CommandAction { +export interface ICommandAction { id: string; title: string; category?: string; @@ -28,8 +27,8 @@ export interface IMenu extends IDisposable { } export interface IMenuItem { - command: CommandAction; - alt?: CommandAction; + command: ICommandAction; + alt?: ICommandAction; when?: KbExpr; group?: string; } @@ -48,7 +47,7 @@ export interface IMenuService { createMenu(id: MenuId, scopedKeybindingService: IKeybindingService): IMenu; - getCommandActions(): CommandAction[]; + getCommandActions(): ICommandAction[]; } export class MenuItemAction extends Actions.Action { @@ -168,7 +167,8 @@ export class DeferredAction extends Actions.Action { private _cachedAction: Actions.IAction; private _emitterUnbind: IDisposable; - constructor(private _instantiationService: Instantiation.IInstantiationService, private _descriptor: Descriptors.AsyncDescriptor0, + constructor(private _instantiationService: Instantiation.IInstantiationService, + private _descriptor: Descriptors.AsyncDescriptor0, id: string, label = '', cssClass = '', enabled = true) { super(id, label, cssClass, enabled); @@ -265,13 +265,15 @@ export class DeferredAction extends Actions.Action { let promise = WinJS.TPromise.as(undefined); return promise.then(() => { return this._instantiationService.createInstance(this._descriptor); + }).then(action => { + if (action instanceof Actions.Action) { + this._cachedAction = action; + // Pipe events from the instantated action through this deferred action + this._emitterUnbind = action.onDidChange(e => this._onDidChange.fire(e)); - }).then((action) => { - Assert.ok(action instanceof Actions.Action, 'Action must be an instanceof Base Action'); - this._cachedAction = action; - - // Pipe events from the instantated action through this deferred action - this._emitterUnbind = this.addEmitter2(this._cachedAction); + } else { + throw new Error('Action must be an instanceof Base Action'); + } return action; }); diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index fd2b6c9c4b9..018654e943a 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -93,17 +93,6 @@ export class ExtensionManagementService implements IExtensionManagementService { this.extensionsPath = environmentService.extensionsPath; this.obsoletePath = path.join(this.extensionsPath, '.obsolete'); this.obsoleteFileLimiter = new Limiter(1); - - // this.disposables = [ - // this.onDidInstallExtension(({ extension, isUpdate, error }) => telemetryService.publicLog( - // isUpdate ? 'extensionGallery2:update' : 'extensionGallery2:install', - // assign(getTelemetryData(extension), { success: !error }) - // )), - // this.onDidUninstallExtension(extension => telemetryService.publicLog( - // 'extensionGallery2:uninstall', - // assign(getTelemetryData(extension), { success: true }) - // )) - // ]; } install(extension: IGalleryExtension): TPromise; diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 22d4183a505..bef2fd60dc2 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -181,7 +181,7 @@ export class ExtHostAPIImplementation { return; } - activeTextEditor.edit((edit: vscode.TextEditorEdit) => { + return activeTextEditor.edit((edit: vscode.TextEditorEdit) => { args.unshift(activeTextEditor, edit); callback.apply(thisArg, args); diff --git a/src/vs/workbench/browser/actions/openSettings.ts b/src/vs/workbench/browser/actions/openSettings.ts index 49ad059f541..68c72f3f08f 100644 --- a/src/vs/workbench/browser/actions/openSettings.ts +++ b/src/vs/workbench/browser/actions/openSettings.ts @@ -12,6 +12,7 @@ import labels = require('vs/base/common/labels'); import {Registry} from 'vs/platform/platform'; import {Action} from 'vs/base/common/actions'; import strings = require('vs/base/common/strings'); +import {EditorOptions} from 'vs/workbench/common/editor'; import {IWorkbenchActionRegistry, Extensions} from 'vs/workbench/common/actionRegistry'; import {StringEditorInput} from 'vs/workbench/common/editor/stringEditorInput'; import {getDefaultValuesContent} from 'vs/platform/configuration/common/model'; @@ -65,8 +66,8 @@ export class BaseTwoEditorsAction extends Action { return this.createIfNotExists(editableResource, defaultEditableContents).then(() => { return this.editorService.createInput({ resource: editableResource }).then((typedRightHandEditableInput) => { const editors = [ - { input: leftHandDefaultInput, position: Position.LEFT }, - { input: typedRightHandEditableInput, position: Position.CENTER } + { input: leftHandDefaultInput, position: Position.LEFT, options: { pinned: true } }, + { input: typedRightHandEditableInput, position: Position.CENTER, options: { pinned: true } } ]; return this.editorService.openEditors(editors).then(() => { @@ -139,7 +140,7 @@ export class OpenGlobalSettingsAction extends BaseOpenSettingsAction { let editorCount = this.editorService.getVisibleEditors().length; return this.editorService.createInput({ resource: this.contextService.toResource('.vscode/settings.json') }).then((typedInput) => { - return this.editorService.openEditor(typedInput, null, editorCount === 2 ? Position.RIGHT : editorCount === 1 ? Position.CENTER : void 0); + return this.editorService.openEditor(typedInput, EditorOptions.create({ pinned: true }), editorCount === 2 ? Position.RIGHT : editorCount === 1 ? Position.CENTER : void 0); }); }) ] diff --git a/src/vs/workbench/browser/media/workbench.css b/src/vs/workbench/browser/media/workbench.css index 468d2dc269d..addf9d6594d 100644 --- a/src/vs/workbench/browser/media/workbench.css +++ b/src/vs/workbench/browser/media/workbench.css @@ -30,6 +30,39 @@ box-sizing: border-box; } +.monaco-workbench .action-bar-select { + width: 100%; + height: 20px; + margin-top: 8px; /* Center the select box */ +} + +.monaco-workbench.windows .action-bar-select { + margin-top: 7px; /* Center the select box */ +} + +#monaco-workbench-drop-overlay { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + z-index: 3000000; +} + +.vs #monaco-workbench-drop-overlay { + background-color: rgba(51,153,255, 0.18); +} + +.vs-dark #monaco-workbench-drop-overlay { + background-color: rgba(83, 89, 93, 0.5); +} + +.hc-black #monaco-workbench-drop-overlay { + background: none !important; + outline: 2px dashed #f38518; + outline-offset: -2px; +} + /* ---------- Light Theme ---------- */ .vs .monaco-workbench { background-color: #F3F3F3; } @@ -44,6 +77,11 @@ background-color: #E1E1E1; } +.vs .monaco-workbench .action-bar-select { + background-color: white; + border-color: #CECECE; +} + /* ---------- Dark Theme ---------- */ .vs-dark .monaco-workbench { @@ -74,6 +112,12 @@ .vs-dark .monaco-workbench .stacked-view .action-label { color: inherit; } .vs-dark .monaco-workbench .stacked-view .action-label:hover { color: #3399FF; } +.vs-dark .monaco-workbench .action-bar-select { + background-color: #3C3C3C; + border-color: #3C3C3C; + color: rgb(204, 204, 204); +} + /* ---------- HC Theme ---------- */ .hc-black .monaco-workbench { color: #FFF; background-color: #000; } @@ -92,4 +136,10 @@ .hc-black .monaco-workbench .monaco-action-bar .action-item.disabled .action-label.disabled { opacity: .4; +} + +.hc-black .monaco-workbench .action-bar-select { + background-color: #3C3C3C; + border-color: #3C3C3C; + color: white; } \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/activitybar/activityAction.ts b/src/vs/workbench/browser/parts/activitybar/activityAction.ts index 24195b0a4ba..ce5e92acb98 100644 --- a/src/vs/workbench/browser/parts/activitybar/activityAction.ts +++ b/src/vs/workbench/browser/parts/activitybar/activityAction.ts @@ -11,14 +11,13 @@ import {Builder, $} from 'vs/base/browser/builder'; import {DelayedDragHandler} from 'vs/base/browser/dnd'; import {Action} from 'vs/base/common/actions'; import {BaseActionItem} from 'vs/base/browser/ui/actionbar/actionbar'; -import {EmitterEvent} from 'vs/base/common/eventEmitter'; import {ProgressBadge, TextBadge, NumberBadge, IconBadge, IBadge} from 'vs/workbench/services/activity/common/activityService'; +import Event, {Emitter} from 'vs/base/common/event'; export class ActivityAction extends Action { - static BADGE = 'badge'; - private badge: IBadge; + private _onDidChangeBadge = new Emitter(); constructor(id: string, name: string, clazz: string) { super(id, name, clazz); @@ -26,17 +25,19 @@ export class ActivityAction extends Action { this.badge = null; } + public get onDidChangeBadge(): Event { + return this._onDidChangeBadge.event; + } + public activate(): void { if (!this.checked) { - this.checked = true; - this.emit('checked', { source: this }); + this._setChecked(true); } } public deactivate(): void { if (this.checked) { - this.checked = false; - this.emit('checked', { source: this }); + this._setChecked(false); } } @@ -46,7 +47,7 @@ export class ActivityAction extends Action { public setBadge(badge: IBadge): void { this.badge = badge; - this.emit(ActivityAction.BADGE, { source: this }); + this._onDidChangeBadge.fire(this); } } @@ -59,12 +60,13 @@ export class ActivityActionItem extends BaseActionItem { private $badge: Builder; private $badgeContent: Builder; - constructor(action: Action, activityName: string = action.label, keybinding: string = null) { + constructor(action: ActivityAction, activityName: string = action.label, keybinding: string = null) { super(null, action); this.cssClass = action.class; this.name = activityName; this._keybinding = keybinding; + action.onDidChangeBadge(this._handleBadgeChangeEvenet, this, this._callOnDispose); } public render(container: HTMLElement): void { @@ -158,7 +160,7 @@ export class ActivityActionItem extends BaseActionItem { } } - public _updateClass(): void { + protected _updateClass(): void { if (this.cssClass) { this.$badge.removeClass(this.cssClass); } @@ -167,7 +169,7 @@ export class ActivityActionItem extends BaseActionItem { this.$badge.addClass(this.cssClass); } - public _updateChecked(): void { + protected _updateChecked(): void { if (this.getAction().checked) { this.$e.addClass('active'); } else { @@ -175,16 +177,14 @@ export class ActivityActionItem extends BaseActionItem { } } - public _updateUnknown(event: EmitterEvent): void { - if (event.getType() === ActivityAction.BADGE) { - let action = this.getAction(); - if (action instanceof ActivityAction) { - this.updateBadge(( action).getBadge()); - } + private _handleBadgeChangeEvenet(): void { + let action = this.getAction(); + if (action instanceof ActivityAction) { + this.updateBadge((action).getBadge()); } } - public _updateEnabled(): void { + protected _updateEnabled(): void { if (this.getAction().enabled) { this.builder.removeClass('disabled'); } else { diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 5d12267d2e5..12a73a2e715 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -17,10 +17,9 @@ import {UntitledEditorInput} from 'vs/workbench/common/editor/untitledEditorInpu import {ResourceEditorInput} from 'vs/workbench/common/editor/resourceEditorInput'; import {IInstantiationService, ServicesAccessor} from 'vs/platform/instantiation/common/instantiation'; import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry'; -import {KbExpr, IKeybindings, IKeybindingService} from 'vs/platform/keybinding/common/keybindingService'; +import {KbExpr, IKeybindings} from 'vs/platform/keybinding/common/keybindingService'; import {TextDiffEditor} from 'vs/workbench/browser/parts/editor/textDiffEditor'; import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService'; -import {IMessageService, Severity, CloseAction} from 'vs/platform/message/common/message'; import {BinaryResourceDiffEditor} from 'vs/workbench/browser/parts/editor/binaryDiffEditor'; import {IEditorGroupService} from 'vs/workbench/services/group/common/groupService'; import {IConfigurationRegistry, Extensions as ConfigurationExtensions} from 'vs/platform/configuration/common/configurationRegistry'; @@ -403,52 +402,4 @@ configurationRegistry.registerConfiguration({ 'description': nls.localize('editorOpenPositioning', "Controls where editors open. Select 'left' or 'right' to open editors to the left or right of the current active one. Select 'first' or 'last' to open editors independently from the currently active one.") } } -}); - -// TODO@Ben remove me next version - -const mapDeprecatedCommands = { - 'workbench.action.focusFirstEditor': 'workbench.action.focusFirstEditorGroup', - 'workbench.action.focusSecondEditor': 'workbench.action.focusSecondEditorGroup', - 'workbench.action.focusThirdEditor': 'workbench.action.focusThirdEditorGroup', - 'workbench.action.focusLeftEditor': 'workbench.action.focusPreviousGroup', - 'workbench.action.focusRightEditor': 'workbench.action.focusNextGroup', - 'workbench.action.moveActiveEditorLeft': 'workbench.action.moveActiveEditorGroupLeft', - 'workbench.action.moveActiveEditorRight': 'workbench.action.moveActiveEditorGroupRight', - 'workbench.action.openPreviousEditor': 'workbench.action.openPreviousEditorFromHistory', - 'workbench.files.action.addToWorkingFiles': 'workbench.action.keepEditor', - 'workbench.files.action.closeAllFiles': 'workbench.action.closeAllEditors', - 'workbench.files.action.closeFile': 'workbench.action.closeActiveEditor', - 'workbench.files.action.closeOtherFiles': 'workbench.action.closeOtherEditors', - 'workbench.files.action.focusWorkingFiles': 'workbench.files.action.focusOpenEditorsView', - 'workbench.files.action.openNextWorkingFile': 'workbench.action.nextEditor', - 'workbench.files.action.openPreviousWorkingFile': 'workbench.action.previousEditor', - 'workbench.files.action.reopenClosedFile': 'workbench.action.reopenClosedEditor', - 'workbench.files.action.workingFilesPicker': 'workbench.action.showAllEditors', - 'workbench.action.cycleEditor': 'workbench.action.navigateEditorGroups' -}; - -Object.keys(mapDeprecatedCommands).forEach(deprecatedCommandId => { - const newCommandId = mapDeprecatedCommands[deprecatedCommandId]; - - KeybindingsRegistry.registerCommandDesc({ - id: deprecatedCommandId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0), - handler(accessor: ServicesAccessor) { - const messageService = accessor.get(IMessageService); - const keybindingService = accessor.get(IKeybindingService); - - messageService.show(Severity.Warning, { - message: nls.localize('commandDeprecated', "Command **{0}** is now deprecated. You can use **{1}** instead", deprecatedCommandId, newCommandId), - actions: [ - CloseAction, - new Action('openKeybindings', nls.localize('openKeybindings', "Configure Keyboard Shortcuts"), null, true, () => { - return keybindingService.executeCommand('workbench.action.openGlobalKeybindings'); - }) - ] - }); - }, - when: undefined, - primary: undefined - }); }); \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/editorPicker.ts b/src/vs/workbench/browser/parts/editor/editorPicker.ts index 5a7a49892e6..84e912c101b 100644 --- a/src/vs/workbench/browser/parts/editor/editorPicker.ts +++ b/src/vs/workbench/browser/parts/editor/editorPicker.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import 'vs/css!./media/editorPicker'; +import 'vs/css!./media/editorpicker'; import {TPromise} from 'vs/base/common/winjs.base'; import nls = require('vs/nls'); import labels = require('vs/base/common/labels'); diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index c27414481ee..f67a5e55181 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -23,7 +23,7 @@ import {UntitledEditorInput} from 'vs/workbench/common/editor/untitledEditorInpu import {IFileEditorInput, EncodingMode, IEncodingSupport, asFileEditorInput, getUntitledOrFileResource} from 'vs/workbench/common/editor'; import {IDisposable, combinedDisposable, dispose} from 'vs/base/common/lifecycle'; import {IMessageService, Severity} from 'vs/platform/message/common/message'; -import {ICommonCodeEditor, IConfigurationChangedEvent, IModelContentChangedEvent, IModelOptionsChangedEvent, IModelModeChangedEvent, ICursorPositionChangedEvent} from 'vs/editor/common/editorCommon'; +import {ICommonCodeEditor, IModelContentChangedEvent, IModelOptionsChangedEvent, IModelModeChangedEvent, ICursorPositionChangedEvent} from 'vs/editor/common/editorCommon'; import {OpenGlobalSettingsAction} from 'vs/workbench/browser/actions/openSettings'; import {ICodeEditor, IDiffEditor} from 'vs/editor/browser/editorBrowser'; import {TrimTrailingWhitespaceAction} from 'vs/editor/contrib/linesOperations/common/linesOperations'; @@ -42,6 +42,7 @@ import {IModeService} from 'vs/editor/common/services/modeService'; import {StyleMutator} from 'vs/base/browser/styleMutator'; import {Selection} from 'vs/editor/common/core/selection'; import {IEditorGroupService} from 'vs/workbench/services/group/common/groupService'; +import {TabFocus} from 'vs/editor/common/config/commonEditorConfig'; function getCodeEditor(editorWidget: IEditor): ICommonCodeEditor { if (editorWidget) { @@ -295,7 +296,8 @@ export class EditorStatus implements IStatusbarItem { } }, this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged()), - this.eventService.addListener2(EventType.RESOURCE_ENCODING_CHANGED, (e: ResourceEvent) => this.onResourceEncodingChange(e.resource)) + this.eventService.addListener2(EventType.RESOURCE_ENCODING_CHANGED, (e: ResourceEvent) => this.onResourceEncodingChange(e.resource)), + TabFocus.onDidChangeTabFocus((e) => this.onTabFocusModeChange()) ); return combinedDisposable(this.toDispose); @@ -428,10 +430,7 @@ export class EditorStatus implements IStatusbarItem { } private onTabFocusModeClick(): void { - let activeEditor = this.editorService.getActiveEditor(); - if (activeEditor instanceof BaseTextEditor && isCodeEditorWithTabFocusMode(activeEditor)) { - (activeEditor.getControl()).updateOptions({ tabFocusMode: false }); - } + TabFocus.setTabFocusMode(false); } private onEditorsChanged(): void { @@ -446,7 +445,6 @@ export class EditorStatus implements IStatusbarItem { this.onModeChange(control); this.onEOLChange(control); this.onEncodingChange(activeEditor); - this.onTabFocusModeChange(activeEditor); this.onIndentationChange(control); // Dispose old active editor listeners @@ -475,11 +473,6 @@ export class EditorStatus implements IStatusbarItem { this.activeEditorListeners.push(control.onDidChangeModelOptions((event: IModelOptionsChangedEvent) => { this.onIndentationChange(control); })); - - // Hook Listener for options changes - this.activeEditorListeners.push(control.onDidChangeConfiguration((event: IConfigurationChangedEvent) => { - this.onTabFocusModeChange(activeEditor); - })); } } @@ -605,13 +598,8 @@ export class EditorStatus implements IStatusbarItem { } } - private onTabFocusModeChange(e: IBaseEditor): void { - let info: StateDelta = { tabFocusMode: false }; - - // We only support text based editors - if (e instanceof BaseTextEditor && isCodeEditorWithTabFocusMode(e)) { - info = { tabFocusMode: true }; - } + private onTabFocusModeChange(): void { + let info: StateDelta = { tabFocusMode: TabFocus.getTabFocusMode() }; this.updateState(info); } @@ -623,19 +611,6 @@ export class EditorStatus implements IStatusbarItem { } } -function isCodeEditorWithTabFocusMode(e: BaseTextEditor): boolean { - let editorWidget = e.getControl(); - if (editorWidget.getEditorType() === EditorType.IDiffEditor) { - editorWidget = (editorWidget).getModifiedEditor(); - } - if (editorWidget.getEditorType() !== EditorType.ICodeEditor) { - return false; - } - - let editorConfig = (editorWidget).getConfiguration(); - return editorConfig.tabFocusMode && !editorConfig.readOnly; -} - function isWritableCodeEditor(e: BaseTextEditor): boolean { let editorWidget = e.getControl(); if (editorWidget.getEditorType() === EditorType.IDiffEditor) { diff --git a/src/vs/workbench/browser/parts/editor/media/editorPicker.css b/src/vs/workbench/browser/parts/editor/media/editorpicker.css similarity index 100% rename from src/vs/workbench/browser/parts/editor/media/editorPicker.css rename to src/vs/workbench/browser/parts/editor/media/editorpicker.css diff --git a/src/vs/workbench/browser/parts/editor/media/sidebyside.css b/src/vs/workbench/browser/parts/editor/media/sidebyside.css index 9f56cdffb90..bc0dea04365 100644 --- a/src/vs/workbench/browser/parts/editor/media/sidebyside.css +++ b/src/vs/workbench/browser/parts/editor/media/sidebyside.css @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.vs .monaco-workbench .editor:not(.empty) > .content { +.vs .monaco-workbench > .editor:not(.empty) > .content { background-color: #F3F3F3; } -.vs-dark .monaco-workbench .editor:not(.empty) > .content { +.vs-dark .monaco-workbench > .editor:not(.empty) > .content { background-color: #252526; } @@ -24,95 +24,109 @@ } .vs #monaco-workbench-editor-drop-overlay, -.vs .monaco-workbench .editor.empty > .content.dropfeedback { +.vs .monaco-workbench > .editor.empty > .content.dropfeedback { background-color: rgba(51,153,255, 0.18); } .vs-dark #monaco-workbench-editor-drop-overlay, -.vs-dark .monaco-workbench .editor.empty > .content.dropfeedback { +.vs-dark .monaco-workbench > .editor.empty > .content.dropfeedback { background-color: rgba(83, 89, 93, 0.5); } -.hc-black #monaco-workbench-editor-drop-overlay, -.hc-black .monaco-workbench .editor.empty > .content.dropfeedback { +.hc-black #monaco-workbench-editor-drop-overlay, +.hc-black .monaco-workbench > .editor.empty > .content.dropfeedback { background: none !important; outline: 2px dashed #f38518; outline-offset: -2px; } -.vs .monaco-workbench .editor > .content.dragging { +.vs .monaco-workbench > .editor > .content.dragging { border-left: 1px solid #E7E7E7; border-right: 1px solid #E7E7E7; } -.vs-dark .monaco-workbench .editor > .content.dragging { +.vs-dark .monaco-workbench > .editor > .content.dragging { border-left: 1px solid #444; border-right: 1px solid #444; } -.monaco-workbench .editor > .content > .one-editor-container { +.monaco-workbench > .editor > .content > .one-editor-container { position: absolute; box-sizing: border-box; /* use border box to be able to draw a border as separator between editors */ } -.monaco-workbench .editor > .content > .editor-left { +.monaco-workbench > .editor > .content > .one-editor-container.editor-left { left: 0; } -.monaco-workbench .editor > .content > .editor-right { +.monaco-workbench > .editor > .content > .one-editor-container.editor-right { right: 0; } -.monaco-workbench .editor > .content > .one-editor-container.drag { +.monaco-workbench > .editor > .content > .one-editor-container.dragging { z-index: 2000000; } -.monaco-workbench .editor > .content > .one-editor-container.dragging { +.monaco-workbench > .editor > .content.dragging > .one-editor-container { box-sizing: content-box; } -.vs .monaco-workbench .editor > .content > .one-editor-container.dragging { +.vs .monaco-workbench > .editor > .content.dragging > .one-editor-container.editor-left, +.vs .monaco-workbench > .editor > .content.dragging > .one-editor-container.editor-center { + border-right: 1px solid #E7E7E7; +} + +.vs .monaco-workbench > .editor > .content > .one-editor-container.dragging { border-left: 1px solid #E7E7E7; border-right: 1px solid #E7E7E7; } -.vs-dark .monaco-workbench .editor > .content > .one-editor-container.dragging { +.vs .monaco-workbench > .editor > .content > .one-editor-container.editor-center, +.vs .monaco-workbench > .editor > .content > .one-editor-container.editor-right, +.vs .monaco-workbench > .editor > .content.dragging > .one-editor-container.editor-right { + border-left: 1px solid #E7E7E7; +} + +.vs-dark .monaco-workbench > .editor > .content.dragging > .one-editor-container.editor-left, +.vs-dark .monaco-workbench > .editor > .content.dragging > .one-editor-container.editor-center { + border-right: 1px solid #444; +} + +.vs-dark .monaco-workbench > .editor > .content > .one-editor-container.dragging { border-left: 1px solid #444; border-right: 1px solid #444; } -.monaco-workbench .editor > .content > .one-editor-container.draggedunder { +.vs-dark .monaco-workbench > .editor > .content > .one-editor-container.editor-center, +.vs-dark .monaco-workbench > .editor > .content > .one-editor-container.editor-right, +.vs-dark .monaco-workbench > .editor > .content.dragging > .one-editor-container.editor-right { + border-left: 1px solid #444; +} + +.hc-black .monaco-workbench > .editor > .content.dragging > .one-editor-container.editor-left, +.hc-black .monaco-workbench > .editor > .content.dragging > .one-editor-container.editor-center { + border-right: 1px solid #6FC3DF; +} + +.hc-black .monaco-workbench > .editor > .content > .one-editor-container.dragging { + border-left: 1px solid #6FC3DF; + border-right: 1px solid #6FC3DF; +} + +.hc-black .monaco-workbench > .editor > .content > .one-editor-container.editor-center, +.hc-black .monaco-workbench > .editor > .content > .one-editor-container.editor-right, +.hc-black .monaco-workbench > .editor > .content.dragging > .one-editor-container.editor-right { + border-left: 1px solid #6FC3DF; +} + +.monaco-workbench > .editor > .content > .one-editor-container.draggedunder { transition: left 200ms ease-out; } -.monaco-workbench .editor > .content > .editor-right.draggedunder { +.monaco-workbench > .editor > .content > .editor-right.draggedunder { transition-property: right; } -.vs .monaco-workbench .editor > .content > .editor-center { - border-left: 1px solid #E7E7E7; -} - -.vs-dark .monaco-workbench .editor > .content > .editor-center { - border-left: 1px solid #444; -} - -.hc-black .monaco-workbench .editor > .content > .editor-center { - border-left: 1px solid #6FC3DF; -} - -.vs .monaco-workbench .editor > .content > .editor-right { - border-left: 1px solid #E7E7E7; -} - -.vs-dark .monaco-workbench .editor > .content > .editor-right { - border-left: 1px solid #444; -} - -.hc-black .monaco-workbench .editor > .content > .editor-right { - border-left: 1px solid #6FC3DF; -} - .monaco-workbench > .editor > .content .editor-container { height: calc(100% - 35px); /* Editor is below editor title */ } \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitle.css b/src/vs/workbench/browser/parts/editor/media/tabstitle.css index 94fa5b3093e..6a784cc0625 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitle.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitle.css @@ -41,6 +41,10 @@ flex: 1; } +.monaco-workbench > .part.editor > .content > .one-editor-container > .title.tabs > .monaco-scrollable-element .scrollbar { + z-index: 3; /* on top of tabs */ +} + /* Tabs Container */ .monaco-workbench > .part.editor > .content > .one-editor-container > .title .tabs-container { @@ -126,12 +130,9 @@ border: 1px solid #f38518; } -.vs .monaco-workbench > .part.editor > .content > .one-editor-container > .title .tabs-container.dropfeedback { - background-color: rgba(51,153,255, 0.18); -} - +.vs .monaco-workbench > .part.editor > .content > .one-editor-container > .title .tabs-container.dropfeedback, .vs .monaco-workbench > .part.editor > .content > .one-editor-container > .title .tabs-container > .tab.dropfeedback { - background-color: rgba(187, 230, 255, 0.5); + background-color: rgba(51,153,255, 0.18); } .vs-dark .monaco-workbench > .part.editor > .content > .one-editor-container > .title .tabs-container.dropfeedback, diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts index 421dfd17da4..b0e50c7d110 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts @@ -984,20 +984,27 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti if (e.target === node) { DOM.EventHelper.stop(e, true); onDrop(e, Position.LEFT); + } else { + DOM.removeClass(node, 'dropfeedback'); } })); // Drag over this.toDispose.push(DOM.addDisposableListener(node, DOM.EventType.DRAG_OVER, (e: DragEvent) => { - DOM.addClass(node, 'dropfeedback'); + if (e.target === node) { + DOM.addClass(node, 'dropfeedback'); + } const target = e.target; if (target) { if (overlay && target.id !== overlayId) { destroyOverlay(); // somehow we managed to move the mouse quickly out of the current overlay, so destroy it } - createOverlay(target); + + if (overlay) { + DOM.removeClass(node, 'dropfeedback'); // if we show an overlay, we can remove the drop feedback from the editor background + } } })); @@ -1655,4 +1662,4 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti this._onGroupFocusChanged.dispose(); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index d7c0f0d01da..133271af37b 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -18,7 +18,7 @@ import {Position} from 'vs/platform/editor/common/editor'; import {IEditorGroup, IEditorIdentifier, asFileEditorInput, EditorOptions} from 'vs/workbench/common/editor'; import {ToolBar} from 'vs/base/browser/ui/toolbar/toolbar'; import {StandardKeyboardEvent} from 'vs/base/browser/keyboardEvent'; -import {CommonKeybindings as Kb} from 'vs/base/common/keyCodes'; +import {CommonKeybindings as Kb, KeyCode} from 'vs/base/common/keyCodes'; import {ActionBar, Separator} from 'vs/base/browser/ui/actionbar/actionbar'; import {IConfigurationService} from 'vs/platform/configuration/common/configuration'; import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService'; @@ -292,11 +292,14 @@ export class TabsTitleControl extends TitleControl { // Add a tab for each opened editor this.context.getEditors().forEach(editor => { + const description = editor.getDescription(true) || ''; + const tabContainer = document.createElement('div'); + tabContainer.title = description; tabContainer.draggable = true; tabContainer.tabIndex = 0; - tabContainer.setAttribute('role', 'tab'); - tabContainer.setAttribute('aria-label', editor.getName()); + tabContainer.setAttribute('role', 'presentation'); // cannot use role "tab" here due to https://github.com/Microsoft/vscode/issues/8659 + tabContainer.setAttribute('aria-label', `tab, ${editor.getName()}`); DOM.addClass(tabContainer, 'tab monaco-editor-background'); tabContainers.push(tabContainer); @@ -308,7 +311,6 @@ export class TabsTitleControl extends TitleControl { // Tab Label const tabLabel = document.createElement('a'); tabLabel.innerText = editor.getName(); - tabLabel.title = editor.getDescription(true) || ''; tabLabelContainer.appendChild(tabLabel); // Tab Close @@ -392,6 +394,16 @@ export class TabsTitleControl extends TitleControl { } })); + // Context menu on Shift+F10 + this.tabDisposeables.push(DOM.addDisposableListener(tab, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { + const event = new StandardKeyboardEvent(e); + if (event.shiftKey && event.keyCode === KeyCode.F10) { + DOM.EventHelper.stop(e); + + this.onContextMenu(identifier, e, tab); + } + })); + // Keyboard accessibility this.tabDisposeables.push(DOM.addDisposableListener(tab, DOM.EventType.KEY_UP, (e: KeyboardEvent) => { const event = new StandardKeyboardEvent(e); @@ -429,6 +441,11 @@ export class TabsTitleControl extends TitleControl { if (handled) { DOM.EventHelper.stop(e, true); } + + // moving in the tabs container can have an impact on scrolling position, so we need to update the custom scrollbar + this.scrollbar.updateState({ + scrollLeft: this.tabsContainer.scrollLeft + }); })); // Pin on double click diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index 6ff4717f0d8..90db1a88951 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -47,7 +47,7 @@ import {IStorageService, StorageScope} from 'vs/platform/storage/common/storage' import {ContextMenuService} from 'vs/workbench/services/contextview/electron-browser/contextmenuService'; import {WorkbenchKeybindingService} from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; import {IWorkspace, IConfiguration} from 'vs/platform/workspace/common/workspace'; -import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingService'; +import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybindingService'; import {IActivityService} from 'vs/workbench/services/activity/common/activityService'; import {IViewletService} from 'vs/workbench/services/viewlet/common/viewletService'; import {IPanelService} from 'vs/workbench/services/panel/common/panelService'; @@ -118,6 +118,8 @@ export class Workbench implements IPartService { private sideBarPosition: Position; private panelHidden: boolean; private editorBackgroundDelayer: Delayer; + private messagesVisibleContext: IKeybindingContextKey; + private editorsVisibleContext: IKeybindingContextKey; constructor( container: HTMLElement, @@ -196,6 +198,10 @@ export class Workbench implements IPartService { this.callbacks.onServicesCreated(); } + // Contexts + this.messagesVisibleContext = this.keybindingService.createKey('globalMessageVisible', false); + this.editorsVisibleContext = this.keybindingService.createKey('editorIsOpen', false); + // Register Listeners this.registerListeners(); @@ -651,10 +657,8 @@ export class Workbench implements IPartService { // Handle message service and quick open events if (this.messageService instanceof WorkbenchMessageService) { - const messagesShowingContextKey = this.keybindingService.createKey('globalMessageVisible', false); - - this.toDispose.push((this.messageService).onMessagesShowing(() => messagesShowingContextKey.set(true))); - this.toDispose.push((this.messageService).onMessagesCleared(() => messagesShowingContextKey.reset())); + this.toDispose.push((this.messageService).onMessagesShowing(() => this.messagesVisibleContext.set(true))); + this.toDispose.push((this.messageService).onMessagesCleared(() => this.messagesVisibleContext.reset())); this.toDispose.push(this.quickOpen.onShow(() => (this.messageService).suspend())); // when quick open is open, don't show messages behind this.toDispose.push(this.quickOpen.onHide(() => (this.messageService).resume())); // resume messages once quick open is closed again @@ -669,8 +673,10 @@ export class Workbench implements IPartService { const editorContainer = this.editorPart.getContainer(); if (visibleEditors === 0) { + this.editorsVisibleContext.reset(); this.editorBackgroundDelayer.trigger(() => editorContainer.addClass('empty')); } else { + this.editorsVisibleContext.set(true); this.editorBackgroundDelayer.trigger(() => editorContainer.removeClass('empty')); } } diff --git a/src/vs/workbench/common/editor/editorStacksModel.ts b/src/vs/workbench/common/editor/editorStacksModel.ts index af809920c78..d0271149301 100644 --- a/src/vs/workbench/common/editor/editorStacksModel.ts +++ b/src/vs/workbench/common/editor/editorStacksModel.ts @@ -992,7 +992,6 @@ export class EditorStacksModel implements IEditorStacksModel { if (modelRaw) { const serialized: ISerializedEditorStacksModel = JSON.parse(modelRaw); - // TODO@Ben remove this once stacks are stable; prevent bad stored state const invalidId = this.doValidate(serialized); if (invalidId) { console.warn(`Ignoring invalid stacks model (Error code: ${invalidId}): ${JSON.stringify(serialized)}`); @@ -1002,55 +1001,6 @@ export class EditorStacksModel implements IEditorStacksModel { this._groups = serialized.groups.map(s => this.doCreateGroup(s)); this._activeGroup = this._groups[serialized.active]; - } else { - this.migrate(); - } - } - - // TODO@Ben migration - private migrate(): void { - const LEGACY_EDITOR_STATE_STORAGE_KEY = 'memento/workbench.parts.editor'; - const legacyModelRaw = this.storageService.get(LEGACY_EDITOR_STATE_STORAGE_KEY, StorageScope.WORKSPACE); - if (legacyModelRaw) { - try { - const legacyModel = JSON.parse(legacyModelRaw); - const state = legacyModel['editorpart.editorState']; - const editorsRaw: { inputId: string; inputValue: string }[] = state.editors; - - const registry = Registry.as(Extensions.Editors); - const editors = editorsRaw.map(editorRaw => { - const factory = registry.getEditorInputFactory(editorRaw.inputId); - if (factory) { - return factory.deserialize(this.instantiationService, editorRaw.inputValue); - } - - return null; - }).filter(editor => !!editor); - - if (editors.length > 0) { - const leftGroup = this.openGroup('', true); - leftGroup.openEditor(editors[0], { active: true, pinned: true }); - } - - if (editors.length > 1) { - const centerGroup = this.openGroup('', true); - centerGroup.openEditor(editors[1], { active: true, pinned: true }); - } - - if (editors.length > 2) { - const rightGroup = this.openGroup('', true); - rightGroup.openEditor(editors[2], { active: true, pinned: true }); - } - - this.storageService.remove(LEGACY_EDITOR_STATE_STORAGE_KEY, StorageScope.WORKSPACE); - } catch (error) { - console.warn('Unable to migrate previous editor state', error, legacyModelRaw); - - // Reset - this._groups = []; - this._activeGroup = void 0; - this.groupToIdentifier = Object.create(null); - } } } diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index d94f1842ac9..b243a61a3c3 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -38,8 +38,7 @@ export class CloseEditorAction extends Action { constructor( id: string, label: string, - @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @IWindowService private windowService: IWindowService + @IWorkbenchEditorService private editorService: IWorkbenchEditorService ) { super(id, label); } @@ -50,8 +49,6 @@ export class CloseEditorAction extends Action { return this.editorService.closeEditor(activeEditor.position, activeEditor.input); } - this.windowService.getWindow().close(); - return TPromise.as(false); } } diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 19f6f2ffa19..6631f839d6b 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -12,9 +12,13 @@ import {IConfigurationRegistry, Extensions as ConfigurationExtensions} from 'vs/ import {IWorkbenchActionRegistry, Extensions} from 'vs/workbench/common/actionRegistry'; import {KeyMod, KeyCode} from 'vs/base/common/keyCodes'; import platform = require('vs/base/common/platform'); -import {KbExpr} from 'vs/platform/keybinding/common/keybindingService'; +import {KbExpr, IKeybindings} from 'vs/platform/keybinding/common/keybindingService'; +import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry'; +import {IWindowService} from 'vs/workbench/services/window/electron-browser/windowService'; import {CloseEditorAction, ReloadWindowAction, ShowStartupPerformance, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleDevToolsAction, ToggleFullScreenAction, ToggleMenuBarAction, OpenRecentAction, CloseFolderAction, CloseWindowAction, NewWindowAction, CloseMessagesAction} from 'vs/workbench/electron-browser/actions'; +const closeEditorOrWindowKeybindings: IKeybindings = { primary: KeyMod.CtrlCmd | KeyCode.KEY_W, win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] }}; + // Contribute Global Actions const viewCategory = nls.localize('view', "View"); const developerCategory = nls.localize('developer', "Developer"); @@ -31,12 +35,24 @@ workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ZoomRe workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowStartupPerformance, ShowStartupPerformance.ID, ShowStartupPerformance.LABEL), 'Developer: Startup Performance', developerCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL), 'Reload Window'); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(CloseMessagesAction, CloseMessagesAction.ID, CloseMessagesAction.LABEL, { primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape] }, KbExpr.has('globalMessageVisible')), 'Close Notification Messages'); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorAction, CloseEditorAction.ID, CloseEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_W, win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] } }), 'View: Close Editor', viewCategory); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorAction, CloseEditorAction.ID, CloseEditorAction.LABEL, closeEditorOrWindowKeybindings), 'View: Close Editor', viewCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleFullScreenAction, ToggleFullScreenAction.ID, ToggleFullScreenAction.LABEL, { primary: KeyCode.F11, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_F } }), 'View: Toggle Full Screen', viewCategory); if (platform.isWindows || platform.isLinux) { workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMenuBarAction, ToggleMenuBarAction.ID, ToggleMenuBarAction.LABEL), 'View: Toggle Menu Bar', viewCategory); } +// close the window when the last editor is closed by reusing the same keybinding +KeybindingsRegistry.registerCommandDesc({ + id: 'workbench.action.closeWindow', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: KbExpr.not('editorIsOpen'), + primary: closeEditorOrWindowKeybindings.primary, + handler: accessor => { + const windowService = accessor.get(IWindowService); + windowService.getWindow().close(); + } +}); + // Configuration: Window const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); configurationRegistry.registerConfiguration({ diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index f6079ff2943..13a0203ff2f 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -8,7 +8,10 @@ import platform = require('vs/base/common/platform'); import URI from 'vs/base/common/uri'; import DOM = require('vs/base/browser/dom'); -import workbenchEditorCommon = require('vs/workbench/common/editor'); +import DND = require('vs/base/browser/dnd'); +import {Builder, $} from 'vs/base/browser/builder'; +import {Identifiers} from 'vs/workbench/common/constants'; +import {asFileEditorInput} from 'vs/workbench/common/editor'; import {IViewletService} from 'vs/workbench/services/viewlet/common/viewletService'; import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService'; import {IStorageService} from 'vs/platform/storage/common/storage'; @@ -17,6 +20,8 @@ import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; import {IEditorGroupService} from 'vs/workbench/services/group/common/groupService'; import {ipcRenderer as ipc, shell, remote} from 'electron'; +import * as fs from 'fs'; +import * as path from 'path'; const dialog = remote.dialog; @@ -29,6 +34,13 @@ export interface IWindowConfiguration { }; } +enum DraggedFileType { + UNKNOWN, + FILE, + EXTENSION, + FOLDER +} + export class ElectronWindow { private win: Electron.BrowserWindow; private windowId: number; @@ -53,7 +65,7 @@ export class ElectronWindow { // React to editor input changes (Mac only) if (platform.platform === platform.Platform.Mac) { this.editorGroupService.onEditorsChanged(() => { - let fileInput = workbenchEditorCommon.asFileEditorInput(this.editorService.getActiveEditorInput(), true); + let fileInput = asFileEditorInput(this.editorService.getActiveEditorInput(), true); let representedFilename = ''; if (fileInput) { representedFilename = fileInput.getResource().fsPath; @@ -63,11 +75,59 @@ export class ElectronWindow { }); } - // Prevent a dropped link from opening within - [DOM.EventType.DRAG_OVER, DOM.EventType.DROP].forEach(event => { + let draggedExternalResources: URI[]; + let dropOverlay: Builder; + + function cleanUp(): void { + draggedExternalResources = void 0; + if (dropOverlay) { + dropOverlay.destroy(); + dropOverlay = void 0; + } + } + + // Detect resources dropped into Code from outside + window.document.body.addEventListener(DOM.EventType.DRAG_OVER, (e: DragEvent) => { + DOM.EventHelper.stop(e); + + if (!draggedExternalResources) { + draggedExternalResources = DND.extractResources(e, true /* external only */); + + // Show Code wide overlay if we detect a Folder or Extension to be dragged + if (draggedExternalResources.some(r => { + const kind = this.getFileKind(r); + + return kind === DraggedFileType.FOLDER || kind === DraggedFileType.EXTENSION; + })) { + dropOverlay = $(window.document.getElementById(Identifiers.WORKBENCH_CONTAINER)) + .div({ id: 'monaco-workbench-drop-overlay' }) + .on(DOM.EventType.DROP, (e: DragEvent) => { + DOM.EventHelper.stop(e, true); + + this.focus(); // make sure this window has focus so that the open call reaches the right window! + ipc.send('vscode:windowOpen', draggedExternalResources.map(r => r.fsPath)); // handled from browser process + + cleanUp(); + }) + .on([DOM.EventType.DRAG_LEAVE, DOM.EventType.DRAG_END], () => { + cleanUp(); + }); + } + } + }); + + // Clear our map and overlay on any finish of DND outside the overlay + [DOM.EventType.DROP, DOM.EventType.DRAG_END].forEach(event => { window.document.body.addEventListener(event, (e: DragEvent) => { - DOM.EventHelper.stop(e); - }); + if (!dropOverlay || e.target !== dropOverlay.getHTMLElement()) { + cleanUp(); // only run cleanUp() if we are not over the overlay (because we are being called in capture phase) + } + }, true /* use capture because components within may preventDefault() when they accept the drop */); + }); + + // prevent opening a real URL inside the shell + window.document.body.addEventListener(DOM.EventType.DROP, (e: DragEvent) => { + DOM.EventHelper.stop(e); }); // Handle window.open() calls @@ -86,20 +146,19 @@ export class ElectronWindow { }; } - public open(pathsToOpen: string[]): void; - public open(fileResource: URI): void; - public open(pathToOpen: string): void; - public open(arg1: any): void { - let pathsToOpen: string[]; - if (Array.isArray(arg1)) { - pathsToOpen = arg1; - } else if (typeof arg1 === 'string') { - pathsToOpen = [arg1]; - } else { - pathsToOpen = [(arg1).fsPath]; + private getFileKind(resource: URI): DraggedFileType { + if (path.extname(resource.fsPath) === '.vsix') { + return DraggedFileType.EXTENSION; } - ipc.send('vscode:windowOpen', pathsToOpen); // handled from browser process + let kind = DraggedFileType.UNKNOWN; + try { + kind = fs.statSync(resource.fsPath).isDirectory() ? DraggedFileType.FOLDER : DraggedFileType.FILE; + } catch (error) { + // Do not fail in DND handler + } + + return kind; } public openNew(): void { diff --git a/src/vs/workbench/parts/debug/browser/debugActionItems.ts b/src/vs/workbench/parts/debug/browser/debugActionItems.ts index c66d289587c..578dfc3dd1d 100644 --- a/src/vs/workbench/parts/debug/browser/debugActionItems.ts +++ b/src/vs/workbench/parts/debug/browser/debugActionItems.ts @@ -8,7 +8,6 @@ import lifecycle = require('vs/base/common/lifecycle'); import errors = require('vs/base/common/errors'); import { TPromise } from 'vs/base/common/winjs.base'; import dom = require('vs/base/browser/dom'); -import { isWindows } from 'vs/base/common/platform'; import { IAction } from 'vs/base/common/actions'; import { BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IDebugService, State } from 'vs/workbench/parts/debug/common/debug'; @@ -27,7 +26,7 @@ export class SelectConfigActionItem extends BaseActionItem { super(null, action); this.select = document.createElement('select'); - this.select.className = `debug-select action-bar-select ${isWindows ? 'windows' : ''}`; + this.select.className = 'debug-select action-bar-select'; this.toDispose = []; this.registerListeners(configurationService); diff --git a/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts b/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts index 0a8a77dc174..ffd25f87aa1 100644 --- a/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts +++ b/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts @@ -83,7 +83,15 @@ export class DebugActionsWidget implements wbext.IWorkbenchContribution { })); $(window).on(dom.EventType.RESIZE, () => this.setXCoordinate(), this.toDispose); - this.dragArea.on(dom.EventType.MOUSE_DOWN, event => { + this.dragArea.on(dom.EventType.MOUSE_UP, (event: MouseEvent) => { + const mouseClickEvent = new StandardMouseEvent(event); + if (mouseClickEvent.detail === 2) { + // double click on debug bar centers it again #8250 + this.setXCoordinate(0.5 * window.innerWidth); + } + }); + + this.dragArea.on(dom.EventType.MOUSE_DOWN, (event: MouseEvent) => { const $window = $(window); this.dragArea.addClass('dragged'); diff --git a/src/vs/workbench/parts/debug/browser/debugViewer.ts b/src/vs/workbench/parts/debug/browser/debugViewer.ts index 9306fcec1ec..82f9290f331 100644 --- a/src/vs/workbench/parts/debug/browser/debugViewer.ts +++ b/src/vs/workbench/parts/debug/browser/debugViewer.ts @@ -632,7 +632,7 @@ export class VariablesRenderer implements tree.IRenderer { } let data: IVariableTemplateData = Object.create(null); - data.expression = dom.append(container, $(isMacintosh ? '.expression.mac' : '.expression.win-linux')); + data.expression = dom.append(container, $('.expression')); data.name = dom.append(data.expression, $('span.name')); data.value = dom.append(data.expression, $('span.value')); @@ -843,7 +843,7 @@ export class WatchExpressionsRenderer implements tree.IRenderer { data.actionBar.push(this.actionProvider.getExpressionActions(), { icon: true, label: false }); } - data.expression = dom.append(container, $(isMacintosh ? '.expression.mac' : '.expression.win-linux')); + data.expression = dom.append(container, $('.expression')); data.name = dom.append(data.expression, $('span.name')); data.value = dom.append(data.expression, $('span.value')); @@ -990,7 +990,7 @@ export class BreakpointsActionProvider implements renderer.IActionProvider { actions.push(this.instantiationService.createInstance(debugactions.RemoveAllBreakpointsAction, debugactions.RemoveAllBreakpointsAction.ID, debugactions.RemoveAllBreakpointsAction.LABEL)); actions.push(new actionbar.Separator()); - actions.push(this.instantiationService.createInstance(debugactions.ToggleBreakpointsActivatedAction, debugactions.ToggleBreakpointsActivatedAction.ID, debugactions.ToggleBreakpointsActivatedAction.LABEL)); + actions.push(this.instantiationService.createInstance(debugactions.ToggleBreakpointsActivatedAction, debugactions.ToggleBreakpointsActivatedAction.ID, debugactions.ToggleBreakpointsActivatedAction.ACTIVATE_LABEL)); actions.push(new actionbar.Separator()); actions.push(this.instantiationService.createInstance(debugactions.EnableAllBreakpointsAction, debugactions.EnableAllBreakpointsAction.ID, debugactions.EnableAllBreakpointsAction.LABEL)); @@ -1099,9 +1099,6 @@ export class BreakpointsRenderer implements tree.IRenderer { data.checkbox = $('input'); data.checkbox.type = 'checkbox'; - if (!isMacintosh) { - data.checkbox.className = 'checkbox-win-linux'; - } dom.append(data.breakpoint, data.checkbox); diff --git a/src/vs/workbench/parts/debug/browser/debugViews.ts b/src/vs/workbench/parts/debug/browser/debugViews.ts index da0596e6aed..07a58995283 100644 --- a/src/vs/workbench/parts/debug/browser/debugViews.ts +++ b/src/vs/workbench/parts/debug/browser/debugViews.ts @@ -387,7 +387,7 @@ export class BreakpointsView extends viewlet.AdaptiveCollapsibleViewletView { public getActions(): actions.IAction[] { return [ this.instantiationService.createInstance(debugactions.AddFunctionBreakpointAction, debugactions.AddFunctionBreakpointAction.ID, debugactions.AddFunctionBreakpointAction.LABEL), - this.instantiationService.createInstance(debugactions.ToggleBreakpointsActivatedAction, debugactions.ToggleBreakpointsActivatedAction.ID, debugactions.ToggleBreakpointsActivatedAction.LABEL), + this.instantiationService.createInstance(debugactions.ToggleBreakpointsActivatedAction, debugactions.ToggleBreakpointsActivatedAction.ID, debugactions.ToggleBreakpointsActivatedAction.ACTIVATE_LABEL), this.instantiationService.createInstance(debugactions.RemoveAllBreakpointsAction, debugactions.RemoveAllBreakpointsAction.ID, debugactions.RemoveAllBreakpointsAction.LABEL) ]; } diff --git a/src/vs/workbench/parts/debug/browser/media/breakpointWidget.css b/src/vs/workbench/parts/debug/browser/media/breakpointWidget.css index 44f4a00ae74..c80b0a7f224 100644 --- a/src/vs/workbench/parts/debug/browser/media/breakpointWidget.css +++ b/src/vs/workbench/parts/debug/browser/media/breakpointWidget.css @@ -18,14 +18,11 @@ line-height: 22px; } -.monaco-editor .breakpoint-widget .input.mac { +.monaco-workbench.mac .monaco-editor .breakpoint-widget .input { font-size: 11px; } -.monaco-editor .breakpoint-widget .input.win { - font-size: 13px; -} - -.monaco-editor .breakpoint-widget .input.linux { +.monaco-workbench.windows .monaco-editor .breakpoint-widget .input, +.monaco-workbench.linux .monaco-editor .breakpoint-widget .input { font-size: 13px; } diff --git a/src/vs/workbench/parts/debug/browser/media/debug.contribution.css b/src/vs/workbench/parts/debug/browser/media/debug.contribution.css index bbc3bd344d8..ae89b981fac 100644 --- a/src/vs/workbench/parts/debug/browser/media/debug.contribution.css +++ b/src/vs/workbench/parts/debug/browser/media/debug.contribution.css @@ -162,11 +162,12 @@ font-family: Monaco, Menlo, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback"; } -.monaco-workbench .monaco-tree-row .expression.mac { +.monaco-workbench.mac .monaco-tree-row .expression { font-size: 11px; } -.monaco-workbench .monaco-tree-row .expression.win-linux { +.monaco-workbench.windows .monaco-tree-row .expression, +.monaco-workbench.linux .monaco-tree-row .expression { font-size: 13px; } diff --git a/src/vs/workbench/parts/debug/browser/media/debugHover.css b/src/vs/workbench/parts/debug/browser/media/debugHover.css index c800790b119..73c87347b2a 100644 --- a/src/vs/workbench/parts/debug/browser/media/debugHover.css +++ b/src/vs/workbench/parts/debug/browser/media/debugHover.css @@ -25,6 +25,7 @@ font-size: 11px; word-break: normal; text-overflow: ellipsis; + height: 18px; overflow: hidden; border-bottom: 1px solid rgba(128, 128, 128, 0.35); } diff --git a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css index a9d95dd62ff..49f1f990a9b 100644 --- a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css @@ -92,7 +92,7 @@ opacity: 0.35; } -.monaco-workbench .debug-select.windows { +.monaco-workbench.windows .debug-select { margin-top: 7px; /* Center the select box */ } @@ -290,7 +290,8 @@ margin-left: 0.8em; } -.debug-viewlet .debug-breakpoints .breakpoint > .checkbox-win-linux { +.monaco-workbench.windows debug-viewlet .debug-breakpoints .breakpoint > .checkbox, +.monaco-workbench.linux debug-viewlet .debug-breakpoints .breakpoint > .checkbox { vertical-align: middle; } diff --git a/src/vs/workbench/parts/debug/browser/media/repl.css b/src/vs/workbench/parts/debug/browser/media/repl.css index f2c1d6cafc2..fe801ad1d02 100644 --- a/src/vs/workbench/parts/debug/browser/media/repl.css +++ b/src/vs/workbench/parts/debug/browser/media/repl.css @@ -35,13 +35,15 @@ word-break: break-all; } -.monaco-workbench .repl .repl-tree .monaco-tree-row .input.expression.mac, -.monaco-workbench .repl .repl-tree .monaco-tree-row .output.expression.mac { +.monaco-workbench.mac .repl .repl-tree .monaco-tree-row .input.expression, +.monaco-workbench.mac .repl .repl-tree .monaco-tree-row .output.expression { font-size: 12px; } -.monaco-workbench .repl .repl-tree .monaco-tree-row .input.expression.win-linux, -.monaco-workbench .repl .repl-tree .monaco-tree-row .output.expression.win-linux { +.monaco-workbench.windows .repl .repl-tree .monaco-tree-row .input.expression, +.monaco-workbench.windows .repl .repl-tree .monaco-tree-row .output.expression, +.monaco-workbench.linux .repl .repl-tree .monaco-tree-row .input.expression, +.monaco-workbench.linux .repl .repl-tree .monaco-tree-row .output.expression { font-size: 14px; } @@ -143,30 +145,30 @@ position: absolute; } -.monaco-workbench .repl .repl-input-wrapper.windows:before { +.monaco-workbench.windows .repl .repl-input-wrapper:before { content: '\203A'; /* character does not exist on windows */ font-size: 22px; } -.monaco-workbench .repl .repl-input-wrapper.mac { +.monaco-workbench.mac .repl .repl-input-wrapper { font-size: 12px; } -.monaco-workbench .repl .repl-input-wrapper.win { +.monaco-workbench.windows .repl .repl-input-wrapper { font-size: 14px; } -.monaco-workbench .repl .repl-input-wrapper.linux { +.monaco-workbench.linux .repl .repl-input-wrapper { font-size: 14px; } -.monaco-workbench .repl .repl-input-wrapper.linux:before { +.monaco-workbench.linux .repl .repl-input-wrapper:before { content: '\276f'; font-size: 9px; line-height: 22px; } -.monaco-workbench .repl .repl-input-wrapper.mac:before { +.monaco-workbench.mac .repl .repl-input-wrapper:before { content: '\276f'; line-height: 22px; } diff --git a/src/vs/workbench/parts/debug/browser/repl.ts b/src/vs/workbench/parts/debug/browser/repl.ts index c19d92a1239..abb31d3d370 100644 --- a/src/vs/workbench/parts/debug/browser/repl.ts +++ b/src/vs/workbench/parts/debug/browser/repl.ts @@ -119,7 +119,7 @@ export class Repl extends Panel { super.create(parent); const container = dom.append(parent.getHTMLElement(), $('.repl')); this.treeContainer = dom.append(container, $('.repl-tree')); - const replInputContainer = dom.append(container, $(platform.isWindows ? '.repl-input-wrapper.windows' : platform.isMacintosh ? '.repl-input-wrapper.mac' : '.repl-input-wrapper.linux')); + const replInputContainer = dom.append(container, $('.repl-input-wrapper')); this.replInput = dom.append(replInputContainer, $('input.repl-input')); this.replInput.type = 'text'; diff --git a/src/vs/workbench/parts/debug/browser/replViewer.ts b/src/vs/workbench/parts/debug/browser/replViewer.ts index 34a488b10a5..fb429e6fae3 100644 --- a/src/vs/workbench/parts/debug/browser/replViewer.ts +++ b/src/vs/workbench/parts/debug/browser/replViewer.ts @@ -29,10 +29,6 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace const $ = dom.emmet; -function getExpressionClassName(): string { - return isMacintosh ? '.expression.mac' : '.expression.win-linux'; -} - export class ReplExpressionsDataSource implements tree.IDataSource { constructor(private debugService: debug.IDebugService) { @@ -155,7 +151,7 @@ export class ReplExpressionsRenderer implements tree.IRenderer { public renderTemplate(tree: tree.ITree, templateId: string, container: HTMLElement): any { if (templateId === ReplExpressionsRenderer.VARIABLE_TEMPLATE_ID) { let data: debugviewer.IVariableTemplateData = Object.create(null); - data.expression = dom.append(container, $(getExpressionClassName())); + data.expression = dom.append(container, $('.expression')); data.name = dom.append(data.expression, $('span.name')); data.value = dom.append(data.expression, $('span.value')); @@ -165,8 +161,8 @@ export class ReplExpressionsRenderer implements tree.IRenderer { if (templateId === ReplExpressionsRenderer.INPUT_OUTPUT_PAIR_TEMPLATE_ID) { let data: IInputOutputPairTemplateData = Object.create(null); dom.addClass(container, 'input-output-pair'); - data.input = dom.append(container, $('.input' + getExpressionClassName())); - data.output = dom.append(container, $('.output' + getExpressionClassName())); + data.input = dom.append(container, $('.input.expression')); + data.output = dom.append(container, $('.output.expression')); data.value = dom.append(data.output, $('span.value')); data.annotation = dom.append(data.output, $('span')); @@ -176,7 +172,7 @@ export class ReplExpressionsRenderer implements tree.IRenderer { if (templateId === ReplExpressionsRenderer.VALUE_OUTPUT_TEMPLATE_ID) { let data: IValueOutputTemplateData = Object.create(null); dom.addClass(container, 'output'); - let expression = dom.append(container, $('.output' + getExpressionClassName())); + let expression = dom.append(container, $('.output.expression')); data.container = container; data.counter = dom.append(expression, $('div.counter')); @@ -190,7 +186,7 @@ export class ReplExpressionsRenderer implements tree.IRenderer { dom.addClass(container, 'output'); data.container = container; - data.expression = dom.append(container, $('.output' + getExpressionClassName())); + data.expression = dom.append(container, $('.output.expression')); data.key = dom.append(data.expression, $('span.name')); data.value = dom.append(data.expression, $('span.value')); data.annotation = dom.append(data.expression, $('span')); diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index 9e9346d9e0e..6713b41b048 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -198,25 +198,19 @@ export interface IEnvConfig { name?: string; type: string; request: string; - program?: string; - stopOnEntry?: boolean; - args?: string[]; - cwd?: string; - runtimeExecutable?: string; - runtimeArgs?: string[]; - env?: { [key: string]: string; }; - sourceMaps?: boolean; - outDir?: string; - address?: string; internalConsoleOptions?: string; - port?: number; preLaunchTask?: string; - externalConsole?: boolean; debugServer?: number; noDebug?: boolean; silentlyAbort?: boolean; } +export interface IExtHostConfig extends IEnvConfig { + port?: number; + sourceMaps?: boolean; + outDir?: string; +} + export interface IConfig extends IEnvConfig { windows?: IEnvConfig; osx?: IEnvConfig; diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index cfc29891b87..d59b0b69fe3 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -337,47 +337,48 @@ export class DebugService implements debug.IDebugService { } private loadBreakpoints(): debug.IBreakpoint[] { + let result: debug.IBreakpoint[]; try { - return JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => { + result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => { return new model.Breakpoint(new Source(breakpoint.source.raw ? breakpoint.source.raw : { path: uri.parse(breakpoint.source.uri).fsPath, name: breakpoint.source.name }), breakpoint.desiredLineNumber || breakpoint.lineNumber, breakpoint.enabled, breakpoint.condition); }); - } catch (e) { - return []; - } + } catch (e) { } + + return result || []; } private loadFunctionBreakpoints(): debug.IFunctionBreakpoint[] { + let result: debug.IFunctionBreakpoint[]; try { - return JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: any) => { + result = JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: any) => { return new model.FunctionBreakpoint(fb.name, fb.enabled); }); - } catch (e) { - return []; - } + } catch (e) { } + + return result || []; } private loadExceptionBreakpoints(): debug.IExceptionBreakpoint[] { - let result: debug.IExceptionBreakpoint[] = null; + let result: debug.IExceptionBreakpoint[]; try { result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => { return new model.ExceptionBreakpoint(exBreakpoint.filter || exBreakpoint.name, exBreakpoint.label, exBreakpoint.enabled); }); - } catch (e) { - result = []; - } + } catch (e) { } - return result; + return result || []; } private loadWatchExpressions(): model.Expression[] { + let result: model.Expression[]; try { - return JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watch: any) => { + result = JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watch: any) => { return new model.Expression(watch.name, false, watch.id); }); - } catch (e) { - return []; - } + } catch (e) { } + + return result || []; } public get state(): debug.State { @@ -565,7 +566,7 @@ export class DebugService implements debug.IDebugService { })))); } - private doCreateSession(configuration: debug.IConfig): TPromise { + private doCreateSession(configuration: debug.IExtHostConfig): TPromise { this.setStateAndEmit(debug.State.Initializing); return this.telemetryService.getTelemetryInfo().then(info => { @@ -722,7 +723,7 @@ export class DebugService implements debug.IDebugService { } this.setStateAndEmit(debug.State.Initializing); - const configuration = this.configurationManager.configuration; + const configuration = this.configurationManager.configuration; return this.doCreateSession({ type: configuration.type, request: 'attach', diff --git a/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts index 40f38f2ce20..7901d414ef9 100644 --- a/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/node/debugConfigurationManager.ts @@ -280,7 +280,7 @@ export class ConfigurationManager implements debug.IConfigurationManager { if (!result) { this.configuration.silentlyAbort = true; } - interactiveVariablesToKeys[interactiveVariable].forEach(key => this.configuration[key] = result); + interactiveVariablesToKeys[interactiveVariable].forEach(key => this.configuration[key] = this.configuration[key].replace(`\${command.${ interactiveVariable }}`, result)); }); } }; @@ -337,6 +337,7 @@ export class ConfigurationManager implements debug.IConfigurationManager { public openConfigFile(sideBySide: boolean): TPromise { const resource = uri.file(paths.join(this.contextService.getWorkspace().resource.fsPath, '/.vscode/launch.json')); + let configFileCreated = false; return this.fileService.resolveContent(resource).then(content => true, err => this.getInitialConfigFileContent().then(content => { @@ -344,10 +345,11 @@ export class ConfigurationManager implements debug.IConfigurationManager { return false; } + configFileCreated = true; return this.fileService.updateContent(resource, content).then(() => true); } - )).then(configFileCreated => { - if (!configFileCreated) { + )).then(errorFree => { + if (!errorFree) { return false; } this.telemetryService.publicLog('debugConfigure'); @@ -355,8 +357,9 @@ export class ConfigurationManager implements debug.IConfigurationManager { return this.editorService.openEditor({ resource: resource, options: { - forceOpen: true - } + forceOpen: true, + pinned: configFileCreated // pin only if config file is created #8727 + }, }, sideBySide).then(() => true); }, (error) => { throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error)); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index b456f786e76..ede9a31dbbe 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -104,9 +104,7 @@ export class CombinedInstallAction extends Action { this.uninstallAction = instantiationService.createInstance(UninstallAction, extension); this.disposables.push(this.installAction, this.uninstallAction); - this.disposables.push(this.installAction.addListener2(Action.ENABLED, () => this.update())); - this.disposables.push(this.installAction.addListener2(Action.LABEL, () => this.update())); - this.disposables.push(this.uninstallAction.addListener2(Action.ENABLED, () => this.update())); + this.installAction.onDidChange(this.update, this, this.disposables); this.update(); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsInput.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsInput.ts index 3f9834f5af2..3617f218fc3 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsInput.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsInput.ts @@ -41,4 +41,8 @@ export class ExtensionsInput extends EditorInput { resolve(refresh?: boolean): TPromise { return TPromise.as(null); } + + supportsSplitEditor(): boolean { + return false; + } } \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts index 023f007edbf..0c58e21e814 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts @@ -98,7 +98,7 @@ export class RatingsWidget implements IDisposable { } private render(): void { - const rating = this.extension.rating; + const rating = Math.round(this.extension.rating * 2) / 2; this.container.innerHTML = ''; if (rating === null) { diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css index d66de89dce6..94e965ec33d 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css @@ -14,7 +14,7 @@ .extension-editor > .header { display: flex; - height: 130px; + height: 128px; background: rgba(128, 128, 128, 0.15); padding: 20px; overflow: hidden; @@ -46,7 +46,6 @@ font-size: 26px; line-height: 30px; font-weight: 600; - line-height: normal; white-space: nowrap; } @@ -102,7 +101,7 @@ } .extension-editor > .body { - height: calc(100% - 170px); + height: calc(100% - 168px); overflow: hidden; } diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css index a7d161f96a2..f169d5c8486 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css @@ -31,7 +31,7 @@ box-sizing: border-box; width: 100%; height: 100%; - padding: 0 9px 0 16px; + padding: 0 11px 0 16px; overflow: hidden; display: flex; } @@ -80,9 +80,8 @@ } .extensions-viewlet > .extensions .extension > .details > .header > .install-count:not(:empty) { - opacity: 0.6; font-size: 80%; - margin-right: 6px; + margin: 0 6px; } .extensions-viewlet > .extensions .extension > .details > .header > .install-count > .octicon { diff --git a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts index f26b6c8c83b..0985de0658d 100644 --- a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts @@ -35,7 +35,6 @@ import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/edito import {IModeService} from 'vs/editor/common/services/modeService'; import {IThemeService} from 'vs/workbench/services/themes/common/themeService'; -const LEGACY_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'editorViewState'; // TODO@Ben migration const TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'textEditorViewState'; interface ITextEditorViewState { @@ -297,12 +296,6 @@ export class TextFileEditor extends BaseTextEditor { } } - // TODO@Ben migration - const legacyEditorViewStateMemento = memento[LEGACY_EDITOR_VIEW_STATE_PREFERENCE_KEY]; - if (legacyEditorViewStateMemento) { - return legacyEditorViewStateMemento[key]; - } - return null; } diff --git a/src/vs/workbench/parts/files/browser/fileTracker.ts b/src/vs/workbench/parts/files/browser/fileTracker.ts index dec14fbfa15..30494df899f 100644 --- a/src/vs/workbench/parts/files/browser/fileTracker.ts +++ b/src/vs/workbench/parts/files/browser/fileTracker.ts @@ -370,7 +370,7 @@ export class FileTracker implements IWorkbenchContribution { private handleDelete(resource: URI): void { if (this.textFileService.isDirty(resource)) { - return; // never dispose dirty resources + return; // never dispose dirty resources from a delete } // Add existing clients matching resource @@ -394,6 +394,9 @@ export class FileTracker implements IWorkbenchContribution { }); inputsContainingPath.forEach((input) => { + if (input.isDirty()) { + return; // never dispose dirty resources from a delete + } // Editor History this.historyService.remove(input); diff --git a/src/vs/workbench/parts/files/browser/files.contribution.ts b/src/vs/workbench/parts/files/browser/files.contribution.ts index 49c431bc825..bb3b62b5bda 100644 --- a/src/vs/workbench/parts/files/browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/browser/files.contribution.ts @@ -252,6 +252,11 @@ configurationRegistry.registerConfiguration({ 'type': 'boolean', 'description': nls.localize('autoReveal', "Controls if the explorer should automatically reveal files when opening them."), 'default': true + }, + 'explorer.enableDragAndDrop': { + 'type': 'boolean', + 'description': nls.localize('enableDragAndDrop', "Controls if the explorer should allow to move files and folders via drag and drop."), + 'default': true } } }); \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts index 54a1e03a7ec..9b49355866d 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts @@ -21,6 +21,7 @@ import {InputBox} from 'vs/base/browser/ui/inputbox/inputBox'; import {$} from 'vs/base/browser/builder'; import platform = require('vs/base/common/platform'); import glob = require('vs/base/common/glob'); +import {IDisposable} from 'vs/base/common/lifecycle'; import {ContributableActionProvider} from 'vs/workbench/browser/actionBarRegistry'; import {LocalFileChangeEvent, IFilesConfiguration, ITextFileService} from 'vs/workbench/parts/files/common/files'; import {IFileOperationResult, FileOperationResult, IFileStat, IFileService} from 'vs/platform/files/common/files'; @@ -38,6 +39,7 @@ import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/edito import {IPartService} from 'vs/workbench/services/part/common/partService'; import {IWorkspaceContextService} from 'vs/workbench/services/workspace/common/contextService'; import {IWorkspace} from 'vs/platform/workspace/common/workspace'; +import {IConfigurationService} from 'vs/platform/configuration/common/configuration'; import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingService'; import {IContextViewService, IContextMenuService} from 'vs/platform/contextview/browser/contextView'; import {IEventService} from 'vs/platform/event/common/event'; @@ -696,6 +698,8 @@ export class FileFilter implements IFilter { // Explorer Drag And Drop Controller export class FileDragAndDrop implements IDragAndDrop { + private toDispose: IDisposable[]; + private dropEnabled: boolean; constructor( @IMessageService private messageService: IMessageService, @@ -703,9 +707,23 @@ export class FileDragAndDrop implements IDragAndDrop { @IEventService private eventService: IEventService, @IProgressService private progressService: IProgressService, @IFileService private fileService: IFileService, + @IConfigurationService private configurationService: IConfigurationService, @IInstantiationService private instantiationService: IInstantiationService, @ITextFileService private textFileService: ITextFileService ) { + this.toDispose = []; + + this.onConfigurationUpdated(configurationService.getConfiguration()); + + this.registerListeners(); + } + + private registerListeners(): void { + this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(e.config))); + } + + private onConfigurationUpdated(config: IFilesConfiguration): void { + this.dropEnabled = config && config.explorer && config.explorer.enableDragAndDrop; } public getDragURI(tree: ITree, stat: FileStat): string { @@ -734,6 +752,10 @@ export class FileDragAndDrop implements IDragAndDrop { } public onDragOver(tree: ITree, data: IDragAndDropData, target: FileStat, originalEvent: DragMouseEvent): IDragOverReaction { + if (!this.dropEnabled) { + return DRAG_OVER_REJECT; + } + let isCopy = originalEvent && ((originalEvent.ctrlKey && !platform.isMacintosh) || (originalEvent.altKey && platform.isMacintosh)); let fromDesktop = data instanceof DesktopDragAndDropData; diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index 131b9ccd294..7f72103631f 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -64,6 +64,7 @@ export interface IFilesConfiguration extends IFilesConfiguration, IWorkbenchEdit dynamicHeight: boolean; }; autoReveal: boolean; + enableDragAndDrop: boolean; }; editor: IEditorOptions; } @@ -247,7 +248,7 @@ export enum AutoSaveMode { export var ITextFileService = createDecorator(TEXT_FILE_SERVICE_ID); export interface IRawTextContent extends IBaseStat { - + /** * The line grouped content of a text file. */ diff --git a/src/vs/workbench/parts/git/browser/views/empty/emptyView.ts b/src/vs/workbench/parts/git/browser/views/empty/emptyView.ts index c7926460bb4..b8394b93303 100644 --- a/src/vs/workbench/parts/git/browser/views/empty/emptyView.ts +++ b/src/vs/workbench/parts/git/browser/views/empty/emptyView.ts @@ -137,7 +137,7 @@ export class EmptyView extends EventEmitter.EventEmitter implements GitView.IVie } public focus():void { - // no-op + this.initButton.focus(); } public layout(dimension:Builder.Dimension):void { diff --git a/src/vs/workbench/parts/output/browser/output.contribution.ts b/src/vs/workbench/parts/output/browser/output.contribution.ts index 99044efe56f..f751bf04993 100644 --- a/src/vs/workbench/parts/output/browser/output.contribution.ts +++ b/src/vs/workbench/parts/output/browser/output.contribution.ts @@ -6,16 +6,19 @@ import 'vs/css!../browser/media/output.contribution'; import nls = require('vs/nls'); import {KeyMod, KeyCode} from 'vs/base/common/keyCodes'; -import {CommonEditorRegistry, EditorActionDescriptor} from 'vs/editor/common/editorCommonExtensions'; import {ModesRegistry} from 'vs/editor/common/modes/modesRegistry'; import platform = require('vs/platform/platform'); -import {SyncActionDescriptor} from 'vs/platform/actions/common/actions'; +import {MenuId, SyncActionDescriptor} from 'vs/platform/actions/common/actions'; import {registerSingleton} from 'vs/platform/instantiation/common/extensions'; import {IWorkbenchActionRegistry, Extensions as ActionExtensions} from 'vs/workbench/common/actionRegistry'; import {OutputService} from 'vs/workbench/parts/output/common/outputServices'; -import {ClearOutputEditorAction, ToggleOutputAction} from 'vs/workbench/parts/output/browser/outputActions'; +import {ToggleOutputAction} from 'vs/workbench/parts/output/browser/outputActions'; import {OUTPUT_MIME, OUTPUT_MODE_ID, OUTPUT_PANEL_ID, IOutputService} from 'vs/workbench/parts/output/common/output'; import panel = require('vs/workbench/browser/panel'); +import {KEYBINDING_CONTEXT_EDITOR_FOCUS, KEYBINDING_CONTEXT_EDITOR_LANGUAGE_ID} from 'vs/editor/common/editorCommon'; +import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry'; +import {KbExpr} from 'vs/platform/keybinding/common/keybindingService'; +import {MenuRegistry} from 'vs/platform/actions/browser/menuService'; // Register Service registerSingleton(IOutputService, OutputService); @@ -44,9 +47,32 @@ let actionRegistry = platform.Registry.as(ActionExtens actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleOutputAction, ToggleOutputAction.ID, ToggleOutputAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_U, linux: { - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_H // On Ubuntu Ctrl+Shift+U is taken by some global OS command + primary: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_H) // On Ubuntu Ctrl+Shift+U is taken by some global OS command } }), 'View: Toggle Output', nls.localize('viewCategory', "View")); -// Contribute to Context Menu of Output Window -CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(ClearOutputEditorAction, ClearOutputEditorAction.ID, nls.localize('clearOutput.label', "Clear Output"), void 0, 'Clear Output')); + +// Define clear command, contribute to editor context menu +{ + const id = 'editor.action.clearoutput'; + + KeybindingsRegistry.registerCommandDesc({ + id, + primary: null, + weight: KeybindingsRegistry.WEIGHT.editorContrib(), + when: KbExpr.has(KEYBINDING_CONTEXT_EDITOR_FOCUS), + handler(accessor) { + accessor.get(IOutputService).getActiveChannel().clear(); + } + }); + + MenuRegistry.addCommand({ + id, + title: nls.localize('clearOutput.label', "Clear Output") + }); + + MenuRegistry.appendMenuItem(MenuId.EditorContext, { + command: MenuRegistry.getCommand(id), + when: KbExpr.equals(KEYBINDING_CONTEXT_EDITOR_LANGUAGE_ID, OUTPUT_MODE_ID) + }); +} diff --git a/src/vs/workbench/parts/output/browser/outputActions.ts b/src/vs/workbench/parts/output/browser/outputActions.ts index 96f7033318e..7e4ea033b9d 100644 --- a/src/vs/workbench/parts/output/browser/outputActions.ts +++ b/src/vs/workbench/parts/output/browser/outputActions.ts @@ -8,10 +8,7 @@ import {TPromise} from 'vs/base/common/winjs.base'; import nls = require('vs/nls'); import {Registry} from 'vs/platform/platform'; import {IAction, Action} from 'vs/base/common/actions'; -import {EditorAction} from 'vs/editor/common/editorAction'; -import {Behaviour} from 'vs/editor/common/editorActionEnablement'; -import {ICommonCodeEditor, IEditorActionDescriptorData} from 'vs/editor/common/editorCommon'; -import {IOutputChannelRegistry, Extensions, IOutputService, OUTPUT_MODE_ID, OUTPUT_PANEL_ID} from 'vs/workbench/parts/output/common/output'; +import {IOutputChannelRegistry, Extensions, IOutputService, OUTPUT_PANEL_ID} from 'vs/workbench/parts/output/common/output'; import {SelectActionItem} from 'vs/base/browser/ui/actionbar/actionbar'; import {IPartService} from 'vs/workbench/services/part/common/partService'; import {IPanelService} from 'vs/workbench/services/panel/common/panelService'; @@ -59,35 +56,6 @@ export class ClearOutputAction extends Action { } } -export class ClearOutputEditorAction extends EditorAction { - - public static ID = 'editor.action.clearoutput'; - - constructor( - descriptor: IEditorActionDescriptorData, - editor: ICommonCodeEditor, - @IOutputService private outputService: IOutputService - ) { - super(descriptor, editor, Behaviour.WidgetFocus | Behaviour.ShowInContextMenu); - } - - public getGroupId(): string { - return 'clear'; - } - - public isSupported(): boolean { - let model = this.editor.getModel(); - let mode = model && model.getMode(); - - return mode && mode.getId() === OUTPUT_MODE_ID && super.isSupported(); - } - - public run(): TPromise { - this.outputService.getActiveChannel().clear(); - return TPromise.as(false); - } -} - export class SwitchOutputAction extends Action { public static ID = 'workbench.output.action.switchBetweenOutputs'; diff --git a/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts index 15333bc1ad3..f71e322b2a4 100644 --- a/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts @@ -67,7 +67,13 @@ class BaseCommandEntry extends QuickOpenEntryGroup { this.keyLabel = keyLabel; this.keyAriaLabel = keyAriaLabel; this.label = label; - this.alias = alias; + + if (label !== alias) { + this.alias = alias; + } else { + aliasHighlights = null; + } + this.setHighlights(labelHighlights, null, aliasHighlights); } diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index 15486e6fa5d..dc1b640994f 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -202,6 +202,7 @@ class ConfigureTaskRunnerAction extends Action { return TPromise.as(undefined); } let sideBySide = !!(event && (event.ctrlKey || event.metaKey)); + let configFileCreated = false; return this.fileService.resolveFile(this.contextService.toResource('.vscode/tasks.json')).then((success) => { return success; }, (err:any) => { @@ -247,6 +248,7 @@ class ConfigureTaskRunnerAction extends Action { if (editorConfig.editor.insertSpaces) { content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + strings.repeat(' ', s2.length * editorConfig.editor.tabSize)); } + configFileCreated = true; return this.fileService.createFile(this.contextService.toResource('.vscode/tasks.json'), content); }); }); @@ -258,7 +260,8 @@ class ConfigureTaskRunnerAction extends Action { return this.editorService.openEditor({ resource: stat.resource, options: { - forceOpen: true + forceOpen: true, + pinned: configFileCreated // pin only if config file is created #8727 } }, sideBySide); }, (error) => { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 2dcbcdc82c9..4f99d11e8cd 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -5,9 +5,11 @@ import DOM = require('vs/base/browser/dom'); import lifecycle = require('vs/base/common/lifecycle'); +import nls = require('vs/nls'); import platform = require('vs/base/common/platform'); import xterm = require('xterm'); import {Dimension} from 'vs/base/browser/builder'; +import {IMessageService, Severity} from 'vs/platform/message/common/message'; import {ITerminalFont} from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper'; import {ITerminalProcess, ITerminalService} from 'vs/workbench/parts/terminal/electron-browser/terminal'; import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; @@ -26,6 +28,7 @@ export class TerminalInstance { private parentDomElement: HTMLElement, private contextService: IWorkspaceContextService, private terminalService: ITerminalService, + private messageService: IMessageService, private onExitCallback: (TerminalInstance) => void ) { this.toDispose = []; @@ -52,7 +55,7 @@ export class TerminalInstance { this.isExiting = true; this.dispose(); if (exitCode) { - console.error('Integrated terminal exited with code ' + exitCode); + this.messageService.show(Severity.Error, nls.localize('terminal.integrated.exitedWithCode', 'The terminal process terminated with exit code: {0}', exitCode)); } this.onExitCallback(this); } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts index b2b659c4fc6..f6920f6b8ff 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts @@ -12,6 +12,7 @@ import {KillTerminalAction, CreateNewTerminalAction, SwitchTerminalInstanceActio import {IActionItem} from 'vs/base/browser/ui/actionbar/actionbar'; import {IConfigurationService} from 'vs/platform/configuration/common/configuration'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; +import {IMessageService} from 'vs/platform/message/common/message'; import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; import {ITerminalProcess, ITerminalService, TERMINAL_PANEL_ID} from 'vs/workbench/parts/terminal/electron-browser/terminal'; import {IThemeService} from 'vs/workbench/services/themes/common/themeService'; @@ -38,7 +39,8 @@ export class TerminalPanel extends Panel { @IInstantiationService private instantiationService: IInstantiationService, @IWorkspaceContextService private contextService: IWorkspaceContextService, @ITerminalService private terminalService: ITerminalService, - @IThemeService private themeService: IThemeService + @IThemeService private themeService: IThemeService, + @IMessageService private messageService: IMessageService ) { super(TERMINAL_PANEL_ID, telemetryService); } @@ -128,7 +130,7 @@ export class TerminalPanel extends Panel { private createTerminal(terminalProcess: ITerminalProcess): TPromise { return new TPromise(resolve => { - var terminalInstance = new TerminalInstance(terminalProcess, this.terminalContainer, this.contextService, this.terminalService, this.onTerminalInstanceExit.bind(this)); + var terminalInstance = new TerminalInstance(terminalProcess, this.terminalContainer, this.contextService, this.terminalService, this.messageService, this.onTerminalInstanceExit.bind(this)); this.terminalInstances.push(terminalInstance); this.setActiveTerminal(this.terminalInstances.length - 1); this.toDispose.push(this.themeService.onDidThemeChange(this.updateTheme.bind(this))); diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 51dc2e3194d..836c71cd1ee 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -626,21 +626,6 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic let entriesRaw = this.storageService.get(HistoryService.STORAGE_KEY, StorageScope.WORKSPACE); if (entriesRaw) { entries = JSON.parse(entriesRaw); - } else { - // TODO@Ben migration - try { - const oldMementoRaw = this.storageService.get('memento/workbench.component.quickopen', StorageScope.WORKSPACE); - if (oldMementoRaw) { - const oldMemento = JSON.parse(oldMementoRaw); - const oldEntries = oldMemento['quickopen.editorhistory'].entries; - - if (oldEntries) { - entries = oldEntries; - } - } - } catch (error) { - console.error(error); - } } this.history = entries.map(entry => {