From 4f2133e6f3c2bad0c2c95c41170a67a46c639628 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 15 Dec 2016 18:00:09 +0100 Subject: [PATCH 001/131] #17292 Improve Copy/Update action - Display the icon in the gutter area - Show sub menu if there are more actions - Highlight line when copied - Highlight line when hovered on aciton - Delayed display of icon on mouse move --- .../preferences/browser/media/preferences.css | 15 +- .../preferences/browser/preferencesEditor.ts | 223 +++++++++--------- .../preferences/browser/preferencesService.ts | 50 ++-- .../preferences/browser/preferencesWidgets.ts | 88 +++++++ .../parts/preferences/common/preferences.ts | 3 +- .../preferences/common/preferencesModels.ts | 13 + 6 files changed, 249 insertions(+), 143 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/media/preferences.css b/src/vs/workbench/parts/preferences/browser/media/preferences.css index f151f537b63..1598092179c 100644 --- a/src/vs/workbench/parts/preferences/browser/media/preferences.css +++ b/src/vs/workbench/parts/preferences/browser/media/preferences.css @@ -147,19 +147,16 @@ background: url(collapsed-dark.svg) 50% 50% no-repeat; } -.monaco-editor .view-line:hover .copySetting:after { - cursor: pointer; - content:" "; +.monaco-editor .copy-preferences-widget { background: url('edit.svg') center center no-repeat; - margin-left: 1em; - display:inline-block; - position: absolute; - height:100%; + transform: rotate(-90deg); width:16px; + height: 16px; + cursor: pointer; } -.monaco-editor.hc-black .view-line:hover .copySetting:after, -.monaco-editor.vs-dark .view-line:hover .copySetting:after { +.monaco-editor.hc-black .copy-preferences-widget, +.monaco-editor.vs-dark .copy-preferences-widget { background: url('edit_inverse.svg') center center no-repeat; } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 2a246672bcf..ade50f9930d 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -33,8 +33,8 @@ import { import { SettingsEditorModel, DefaultSettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels'; import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; import { ICodeEditor, IEditorMouseEvent, IEditorContributionCtor } from 'vs/editor/browser/editorBrowser'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { DefaultSettingsHeaderWidget, SettingsGroupTitleWidget, SettingsCountWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; +import { IContextMenuService, ContextSubMenu } from 'vs/platform/contextview/browser/contextView'; +import { DefaultSettingsHeaderWidget, SettingsGroupTitleWidget, SettingsCountWidget, CopyPreferenceWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { CommonEditorRegistry, EditorCommand } from 'vs/editor/common/editorCommonExtensions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -50,6 +50,7 @@ import { IMessageService } from 'vs/platform/message/common/message'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { ITextModelResolverService } from 'vs/editor/common/services/resolverService'; +import { RangeHighlightDecorations } from 'vs/workbench/common/editor/rangeDecorations'; // Ignore following contributions import { FoldingController } from 'vs/editor/contrib/folding/browser/folding'; @@ -330,7 +331,7 @@ export class SettingsEditorContribution extends PreferencesEditorContribution im export class SettingsRenderer extends Disposable implements IPreferencesRenderer { - private copySettingActionRenderer: CopySettingActionRenderer; + private copySettingActionRenderer: CopySettingRenderer; private modelChangeDelayer: Delayer = new Delayer(200); constructor(protected editor: ICodeEditor, protected settingsEditorModel: SettingsEditorModel, @@ -338,7 +339,7 @@ export class SettingsRenderer extends Disposable implements IPreferencesRenderer @IInstantiationService protected instantiationService: IInstantiationService ) { super(); - this.copySettingActionRenderer = this._register(instantiationService.createInstance(CopySettingActionRenderer, editor, false)); + this.copySettingActionRenderer = this._register(instantiationService.createInstance(CopySettingRenderer, editor, false)); this._register(editor.getModel().onDidChangeContent(() => this.modelChangeDelayer.trigger(() => this.onModelChanged()))); } @@ -363,7 +364,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR private filteredMatchesRenderer: FilteredMatchesRenderer; private focusNextSettingRenderer: FocusNextSettingRenderer; private hiddenAreasRenderer: HiddenAreasRenderer; - private copySettingActionRenderer: CopySettingActionRenderer; + private copySettingActionRenderer: CopySettingRenderer; private settingsCountWidget: SettingsCountWidget; constructor(protected editor: ICodeEditor, protected settingsEditorModel: DefaultSettingsEditorModel, @@ -376,7 +377,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR this.settingsGroupTitleRenderer = this._register(instantiationService.createInstance(SettingsGroupTitleRenderer, editor)); this.filteredMatchesRenderer = this._register(instantiationService.createInstance(FilteredMatchesRenderer, editor)); this.focusNextSettingRenderer = this._register(instantiationService.createInstance(FocusNextSettingRenderer, editor)); - this.copySettingActionRenderer = this._register(instantiationService.createInstance(CopySettingActionRenderer, editor, true)); + this.copySettingActionRenderer = this._register(instantiationService.createInstance(CopySettingRenderer, editor, true)); this.settingsCountWidget = this._register(instantiationService.createInstance(SettingsCountWidget, editor, this.getCount(settingsEditorModel.settingsGroups))); const paranthesisHidingRenderer = this._register(instantiationService.createInstance(StaticContentHidingRenderer, editor, settingsEditorModel.settingsGroups)); this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, paranthesisHidingRenderer])); @@ -753,122 +754,129 @@ export class FocusNextSettingRenderer extends Disposable { } } -export class CopySettingActionRenderer extends Disposable { +export class CopySettingRenderer extends Disposable { + + private copyPreferenceWidgetForCusorPosition: CopyPreferenceWidget; + private copyPreferenceWidgetForMouseMove: CopyPreferenceWidget; - private decorationIds: string[] = []; private settingsGroups: ISettingsGroup[]; - private model: editorCommon.IModel; + private toggleCopyPreferenceWidgetOnMouseMoveDelayer: Delayer; + + private copyRangeHighlighter: RangeHighlightDecorations; constructor(private editor: ICodeEditor, private isDefaultSettings: boolean, - @IPreferencesService private settingsService: IPreferencesService, + @IPreferencesService private preferencesService: IPreferencesService, + @IInstantiationService private instantiationService: IInstantiationService, @IContextMenuService private contextMenuService: IContextMenuService ) { super(); - this._register(editor.onMouseUp(e => this.onEditorMouseUp(e))); + this.copyRangeHighlighter = this._register(instantiationService.createInstance(RangeHighlightDecorations)); + + this.copyPreferenceWidgetForCusorPosition = this._register(this.instantiationService.createInstance(CopyPreferenceWidget, editor)); + this.copyPreferenceWidgetForMouseMove = this._register(this.instantiationService.createInstance(CopyPreferenceWidget, editor)); + this.toggleCopyPreferenceWidgetOnMouseMoveDelayer = new Delayer(50); + + this._register(this.copyPreferenceWidgetForCusorPosition.onClick(setting => this.onEditSettingClicked(this.copyPreferenceWidgetForCusorPosition))); + this._register(this.copyPreferenceWidgetForMouseMove.onClick(setting => this.onEditSettingClicked(this.copyPreferenceWidgetForMouseMove))); + + this._register(this.copyPreferenceWidgetForCusorPosition.onMouseOver(setting => this.onMouseOver(this.copyPreferenceWidgetForCusorPosition))); + this._register(this.copyPreferenceWidgetForMouseMove.onMouseOver(setting => this.onMouseOver(this.copyPreferenceWidgetForMouseMove))); + + this._register(this.editor.onDidChangeCursorPosition(positionChangeEvent => this.onPositionChanged(positionChangeEvent))); + this._register(this.editor.onMouseMove(mouseMoveEvent => this.onMouseMoved(mouseMoveEvent))); } public render(settingsGroups: ISettingsGroup[]): void { - this.model = this.editor.getModel(); + this.copyPreferenceWidgetForCusorPosition.hide(); + this.copyPreferenceWidgetForMouseMove.hide(); this.settingsGroups = settingsGroups; - this.model.changeDecorations(changeAccessor => { - this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, []); - }); - this.model.changeDecorations(changeAccessor => { - this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, this.createDecorations(this.model)); + + const settings = this.getSettings(this.editor.getPosition().lineNumber); + if (settings.length) { + this.showCopyPreferencesWidget(this.copyPreferenceWidgetForCusorPosition, settings); + } + } + + private onPositionChanged(positionChangeEvent: editorCommon.ICursorPositionChangedEvent) { + this.copyPreferenceWidgetForMouseMove.hide(); + const settings = this.getSettings(positionChangeEvent.position.lineNumber); + if (settings.length) { + this.showCopyPreferencesWidget(this.copyPreferenceWidgetForCusorPosition, settings); + } else { + this.copyPreferenceWidgetForCusorPosition.hide(); + } + } + + private onMouseMoved(mouseMoveEvent: IEditorMouseEvent): void { + if (mouseMoveEvent.event.target === this.copyPreferenceWidgetForMouseMove.getDomNode() || + mouseMoveEvent.event.target === this.copyPreferenceWidgetForCusorPosition.getDomNode() + ) { + return; + } + this.copyRangeHighlighter.removeHighlightRange(); + const settings = mouseMoveEvent.target.position ? this.getSettings(mouseMoveEvent.target.position.lineNumber) : null; + if (settings && settings.length && mouseMoveEvent.target.position.lineNumber !== this.copyPreferenceWidgetForCusorPosition.getLine()) { + this.showCopyPreferencesWidget(this.copyPreferenceWidgetForMouseMove, settings); + } else { + this.copyPreferenceWidgetForMouseMove.hide(); + } + } + + private showCopyPreferencesWidget(copyPreferencesWidget: CopyPreferenceWidget, settings: ISetting[]) { + copyPreferencesWidget.show(settings[0].valueRange.startLineNumber, settings); + copyPreferencesWidget.getDomNode().title = this.isDefaultSettings ? nls.localize('copyTitle', "Copy to settings") : nls.localize('updateTitle', "Update"); + } + + private getSettings(lineNumber: number): ISetting[] { + const configurationMap = this.getConfigurationsMap(); + return this.getSettingsAtLineNumber(lineNumber).filter(setting => { + let jsonSchema: IJSONSchema = configurationMap[setting.key]; + return jsonSchema && (this.isDefaultSettings || jsonSchema.type === 'boolean' || jsonSchema.enum); }); } - private createDecorations(model: editorCommon.IModel): editorCommon.IModelDeltaDecoration[] { - let result: editorCommon.IModelDeltaDecoration[] = []; - for (const settingsGroup of this.settingsGroups) { - for (const settingsSection of settingsGroup.sections) { - for (const setting of settingsSection.settings) { - const decoration = this.createSettingDecoration(setting, model); - if (decoration) { - result.push(decoration); - } - } - } - } - return result; - } - - private createSettingDecoration(setting: ISetting, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration { - const jsonSchema: IJSONSchema = this.getConfigurationsMap()[setting.key]; - if (jsonSchema) { - const canChooseValue = jsonSchema.enum || jsonSchema.type === 'boolean'; - if (this.isDefaultSettings || canChooseValue) { - const lineNumber = setting.keyRange.startLineNumber; - return { - range: { - startLineNumber: lineNumber, - startColumn: model.getLineMaxColumn(lineNumber), - endLineNumber: lineNumber, - endColumn: model.getLineMaxColumn(lineNumber), - }, - options: { - afterContentClassName: 'copySetting', - stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - hoverMessage: canChooseValue ? this.isDefaultSettings ? nls.localize('selectAndCopySetting', "Select a value and copy to Settings") - : nls.localize('selectValue', "Select a value") : nls.localize('copy', "Copy to Settings") - } - }; - } - } - return null; - } - - private onEditorMouseUp(e: IEditorMouseEvent): void { - let range = e.target.range; - if (!range || !range.isEmpty) { - return; - } - if (!e.event.leftButton) { - return; - } - - switch (e.target.type) { - case editorCommon.MouseTargetType.CONTENT_EMPTY: - if (DOM.hasClass(e.target.element, 'copySetting')) { - this.onClick(e); - } - return; - default: - return; - } - } - - private getConfigurationsMap(): { [qualifiedKey: string]: IJSONSchema } { - return Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); - } - - private onClick(e: IEditorMouseEvent) { - const setting = this.getSetting(e.target.range.startLineNumber); - if (setting) { - let jsonSchema: IJSONSchema = this.getConfigurationsMap()[setting.key]; - const actions = this.getActions(setting, jsonSchema); - let elementPosition = DOM.getDomNodePagePosition(e.target.element); - const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 10 }; - this.contextMenuService.showContextMenu({ - getAnchor: () => anchor, - getActions: () => TPromise.wrap(actions) - }); - } - } - - private getSetting(lineNumber: number): ISetting { + private getSettingsAtLineNumber(lineNumber: number): ISetting[] { + const settings = []; for (const group of this.settingsGroups) { + if (group.range.startLineNumber > lineNumber) { + break; + } if (lineNumber >= group.range.startLineNumber && lineNumber <= group.range.endLineNumber) { for (const section of group.sections) { for (const setting of section.settings) { - if (lineNumber >= setting.keyRange.startLineNumber && lineNumber <= setting.keyRange.endLineNumber) { - return setting; + if (setting.range.startLineNumber > lineNumber) { + break; + } + if (lineNumber >= setting.range.startLineNumber && lineNumber <= setting.range.endLineNumber) { + settings.push(setting); } } } } } - return null; + return settings; + } + + private onMouseOver(copyPreferenceWidget: CopyPreferenceWidget): void { + this.copyRangeHighlighter.highlightRange({ + resource: this.editor.getModel().uri, + range: copyPreferenceWidget.preferences[0].valueRange + }, this.editor); + } + + private onEditSettingClicked(copyPreferenceWidget: CopyPreferenceWidget): void { + const elementPosition = DOM.getDomNodePagePosition(copyPreferenceWidget.getDomNode()); + const anchor = { x: elementPosition.left + elementPosition.width, y: elementPosition.top + elementPosition.height + 10 }; + const actions = copyPreferenceWidget.preferences.length === 1 ? this.getActions(copyPreferenceWidget.preferences[0], this.getConfigurationsMap()[copyPreferenceWidget.preferences[0].key]) + : copyPreferenceWidget.preferences.map(setting => new ContextSubMenu(setting.key, this.getActions(setting, this.getConfigurationsMap()[setting.key]))); + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => TPromise.wrap(actions) + }); + } + + private getConfigurationsMap(): { [qualifiedKey: string]: IJSONSchema } { + return Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); } private getActions(setting: ISetting, jsonSchema: IJSONSchema): IAction[] { @@ -877,12 +885,12 @@ export class CopySettingActionRenderer extends Disposable { id: 'truthyValue', label: 'true', enabled: true, - run: () => this.settingsService.copyConfiguration({ key: setting.key, value: true }) + run: () => this.updateSetting(setting, true) }, { id: 'falsyValue', label: 'false', enabled: true, - run: () => this.settingsService.copyConfiguration({ key: setting.key, value: false }) + run: () => this.updateSetting(setting, false) }]; } if (jsonSchema.enum) { @@ -891,7 +899,7 @@ export class CopySettingActionRenderer extends Disposable { id: value, label: JSON.stringify(value), enabled: true, - run: () => this.settingsService.copyConfiguration({ key: setting.key, value }) + run: () => this.updateSetting(setting, value) }; }); } @@ -899,18 +907,17 @@ export class CopySettingActionRenderer extends Disposable { id: 'copyToSettings', label: nls.localize('copyToSettings', "Copy to Settings"), enabled: true, - run: () => this.settingsService.copyConfiguration(setting) + run: () => this.updateSetting(setting, setting.value) }]; } - public dispose() { - if (this.model) { - this.model.deltaDecorations(this.decorationIds, []); - } - super.dispose(); + private updateSetting(setting: ISetting, value: any): void { + this.copyRangeHighlighter.removeHighlightRange(); + this.preferencesService.updateSetting(setting, value); } } + const DefaultSettingsEditorCommand = EditorCommand.bindToContribution((editor: editorCommon.ICommonCodeEditor) => editor.getContribution(PreferencesEditorContribution.ID)); CommonEditorRegistry.registerEditorCommand(new DefaultSettingsEditorCommand({ diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts index c9971e6feab..8b14f5ce0d7 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -10,7 +10,6 @@ import URI from 'vs/base/common/uri'; import { LinkedMap as Map } from 'vs/base/common/map'; import * as labels from 'vs/base/common/labels'; import { Disposable } from 'vs/base/common/lifecycle'; -import { parseTree, findNodeAtLocation } from 'vs/base/common/json'; import { asFileEditorInput, SideBySideEditorInput, EditorInput } from 'vs/workbench/common/editor'; import { StringEditorInput } from 'vs/workbench/common/editor/stringEditorInput'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -25,13 +24,13 @@ import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { IConfigurationEditingService, ConfigurationTarget, IConfigurationValue } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { IPreferencesService, IPreferencesEditorModel } from 'vs/workbench/parts/preferences/common/preferences'; +import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IPreferencesService, IPreferencesEditorModel, ISetting } from 'vs/workbench/parts/preferences/common/preferences'; import { SettingsEditorModel, DefaultSettingsEditorModel, DefaultKeybindingsEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { DefaultPreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; import { ITextModelResolverService } from 'vs/editor/common/services/resolverService'; +import { RangeHighlightDecorations } from 'vs/workbench/common/editor/rangeDecorations'; const SETTINGS_INFO_IGNORE_KEY = 'settings.workspace.info.ignore'; @@ -55,6 +54,8 @@ export class PreferencesService extends Disposable implements IPreferencesServic private defaultSettingsEditorInputForUser: DefaultPreferencesEditorInput; private defaultSettingsEditorInputForWorkspace: DefaultPreferencesEditorInput; + private copyRangeHighlighter: RangeHighlightDecorations; + constructor( @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @IEditorGroupService private editorGroupService: IEditorGroupService, @@ -73,6 +74,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic ) { super(); this.defaultPreferencesEditorModels = new Map(); + this.copyRangeHighlighter = this.instantiationService.createInstance(RangeHighlightDecorations); } createDefaultPreferencesEditorModel(uri: URI): TPromise { @@ -152,19 +154,31 @@ export class PreferencesService extends Disposable implements IPreferencesServic })); } - public copyConfiguration(configurationValue: IConfigurationValue): void { + public updateSetting(setting: ISetting, value: any): void { const configurationTarget = this.getConfigurationTargetForCurrentActiveEditor(); if (configurationTarget !== null) { - this.telemetryService.publicLog('defaultSettingsActions.copySetting', { userConfigurationKeys: [configurationValue.key] }); - const editorControl = this.editorService.getActiveEditor().getControl(); - this.configurationEditingService.writeConfiguration(configurationTarget, configurationValue, { writeToBuffer: true, autoSave: true }) - .then(() => { - editorControl.focus(); - editorControl.setSelection(this.getSelectionRange(configurationValue.key, editorControl.getModel())); - }, error => this.messageService.show(Severity.Error, error)); + this.telemetryService.publicLog('defaultSettingsActions.copySetting', { userConfigurationKeys: [setting.key] }); + this.configurationEditingService.writeConfiguration(configurationTarget, { key: setting.key, value }, { writeToBuffer: true, autoSave: true }) + .then(() => this.onSettingUpdated(setting, configurationTarget), error => this.messageService.show(Severity.Error, error)); } } + private onSettingUpdated(setting: ISetting, configurationTarget: ConfigurationTarget) { + const editorControl = this.editorService.getActiveEditor().getControl(); + editorControl.focus(); + this.resolvePreferencesEditorModel(this.getEditableSettingsURI(configurationTarget)) + .then(editorModel => { + const settingsEditorModel: SettingsEditorModel = editorModel; + setting = settingsEditorModel.getSetting(setting.key); + editorControl.setSelection(setting.valueRange); + this.copyRangeHighlighter.highlightRange({ + resource: editorModel.uri, + range: setting.range, + isWholeLine: false + }, editorControl); + }); + } + private resolveSettingsEditorModel(configurationTarget: ConfigurationTarget): TPromise { const settingsUri = this.getEditableSettingsURI(configurationTarget); if (settingsUri) { @@ -287,18 +301,6 @@ export class PreferencesService extends Disposable implements IPreferencesServic return null; } - private getSelectionRange(setting: string, model: editorCommon.IModel): editorCommon.IRange { - const tree = parseTree(model.getValue()); - const node = findNodeAtLocation(tree, [setting]); - const position = model.getPositionAt(node.offset); - return { - startLineNumber: position.lineNumber, - startColumn: position.column, - endLineNumber: position.lineNumber, - endColumn: position.column + node.length - }; - } - private fetchMostCommonlyUsedSettings(): TPromise { return TPromise.wrap([ 'editor.fontSize', diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts index 78a0c801700..17343c40433 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -332,4 +332,92 @@ export class SettingsCountWidget extends Widget implements IOverlayWidget { preference: null }; } +} + +export class CopyPreferenceWidget extends Widget implements IOverlayWidget { + + private static counter: number = 1; + + private _domNode: HTMLElement; + private _visible: boolean; + private _line: number; + private _id: string; + private _preferences: T[]; + + private _onClick: Emitter = new Emitter(); + public get onClick(): Event { return this._onClick.event; } + + private _onMouseOver: Emitter = new Emitter(); + public get onMouseOver(): Event { return this._onMouseOver.event; } + + constructor(private editor: ICodeEditor, + @IContextMenuService contextMenuService: IContextMenuService + ) { + super(); + this._id = 'preferences.copyPreferenceWidget' + CopyPreferenceWidget.counter++; + this.editor.addOverlayWidget(this); + this._register(this.editor.onDidScrollChange(() => { + if (this._visible) { + this._layout(); + } + })); + } + + public dispose(): void { + this.editor.removeOverlayWidget(this); + super.dispose(); + } + + getId(): string { + return this._id; + } + + getDomNode(): HTMLElement { + if (!this._domNode) { + this._domNode = document.createElement('div'); + this._domNode.style.width = '20px'; + this._domNode.style.height = '20px'; + this._domNode.className = 'copy-preferences-widget hidden'; + this.onclick(this._domNode, e => this._onClick.fire()); + this.onmouseover(this._domNode, e => this._onMouseOver.fire()); + } + return this._domNode; + } + + getPosition(): IOverlayWidgetPosition { + return null; + } + + getLine(): number { + return this._line; + } + + show(line: number, preferences: T[]): void { + this._preferences = preferences; + if (!this._visible || this._line !== line) { + this._line = line; + this._visible = true; + this._layout(); + } + } + + get preferences(): T[] { + return this._preferences; + } + + hide(): void { + if (this._visible) { + this._visible = false; + this._domNode.classList.add('hidden'); + } + } + + private _layout(): void { + const topForLineNumber = this.editor.getTopForLineNumber(this._line); + const editorScrollTop = this.editor.getScrollTop(); + + this._domNode.style.top = `${topForLineNumber - editorScrollTop - 2}px`; + this._domNode.style.left = '0px'; + this._domNode.classList.remove('hidden'); + } } \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index d8218a1a5e2..17ef49ad855 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -8,7 +8,6 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { LinkedMap as Map } from 'vs/base/common/map'; import { IRange } from 'vs/editor/common/editorCommon'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IConfigurationValue } from 'vs/workbench/services/configuration/common/configurationEditing'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; export interface ISettingsGroup { @@ -67,7 +66,7 @@ export interface IPreferencesService { openWorkspaceSettings(): TPromise; openGlobalKeybindingSettings(): TPromise; - copyConfiguration(configurationValue: IConfigurationValue): void; + updateSetting(setting: ISetting, value: any): void; } export const CONTEXT_DEFAULT_SETTINGS_EDITOR = new RawContextKey('defaultSettingsEditor', false); diff --git a/src/vs/workbench/parts/preferences/common/preferencesModels.ts b/src/vs/workbench/parts/preferences/common/preferencesModels.ts index fb3d68dd0fc..a06097b1ea8 100644 --- a/src/vs/workbench/parts/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/parts/preferences/common/preferencesModels.ts @@ -88,6 +88,19 @@ export abstract class AbstractSettingsModel extends Disposable { return null; } + public getSetting(key: string): ISetting { + for (const group of this.settingsGroups) { + for (const section of group.sections) { + for (const setting of section.settings) { + if (key === setting.key) { + return setting; + } + } + } + } + return null; + } + protected abstract _findMatchesInSetting(searchString: string, searchRegex: RegExp, setting: ISetting): IRange[]; public abstract settingsGroups: ISettingsGroup[]; } From f0531d41c55f333cb85461da6864b6e788489731 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 16 Dec 2016 16:58:27 +0100 Subject: [PATCH 002/131] #17292 Settings action improvements - Improve highlighting when hovered on action - Improve highlighting when updating setting - Improve hover text of action --- .../preferences/browser/media/preferences.css | 6 +- .../preferences/browser/preferencesEditor.ts | 273 +++++++++++++----- .../preferences/browser/preferencesService.ts | 72 +---- .../preferences/browser/preferencesWidgets.ts | 6 +- .../parts/preferences/common/preferences.ts | 6 +- 5 files changed, 218 insertions(+), 145 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/media/preferences.css b/src/vs/workbench/parts/preferences/browser/media/preferences.css index 1598092179c..47618a057d1 100644 --- a/src/vs/workbench/parts/preferences/browser/media/preferences.css +++ b/src/vs/workbench/parts/preferences/browser/media/preferences.css @@ -147,7 +147,7 @@ background: url(collapsed-dark.svg) 50% 50% no-repeat; } -.monaco-editor .copy-preferences-widget { +.monaco-editor .edit-preferences-widget { background: url('edit.svg') center center no-repeat; transform: rotate(-90deg); width:16px; @@ -155,8 +155,8 @@ cursor: pointer; } -.monaco-editor.hc-black .copy-preferences-widget, -.monaco-editor.vs-dark .copy-preferences-widget { +.monaco-editor.hc-black .edit-preferences-widget, +.monaco-editor.vs-dark .edit-preferences-widget { background: url('edit_inverse.svg') center center no-repeat; } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index ade50f9930d..f93e27a9366 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -17,7 +17,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import Event, { Emitter } from 'vs/base/common/event'; import { LinkedMap as Map } from 'vs/base/common/map'; import { Registry } from 'vs/platform/platform'; -import { EditorOptions, EditorInput } from 'vs/workbench/common/editor'; +import { EditorOptions, EditorInput, asFileEditorInput } from 'vs/workbench/common/editor'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; @@ -28,13 +28,13 @@ import { Range } from 'vs/editor/common/core/range'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IPreferencesService, ISettingsGroup, ISetting, IPreferencesEditorModel, IFilterResult, CONTEXT_DEFAULT_SETTINGS_EDITOR, - DEFAULT_EDITOR_COMMAND_COLLAPSE_ALL + DEFAULT_EDITOR_COMMAND_COLLAPSE_ALL, ISettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferences'; import { SettingsEditorModel, DefaultSettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels'; import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; import { ICodeEditor, IEditorMouseEvent, IEditorContributionCtor } from 'vs/editor/browser/editorBrowser'; import { IContextMenuService, ContextSubMenu } from 'vs/platform/contextview/browser/contextView'; -import { DefaultSettingsHeaderWidget, SettingsGroupTitleWidget, SettingsCountWidget, CopyPreferenceWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; +import { DefaultSettingsHeaderWidget, SettingsGroupTitleWidget, SettingsCountWidget, EditPreferenceWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { CommonEditorRegistry, EditorCommand } from 'vs/editor/common/editorCommonExtensions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -46,11 +46,12 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEventService } from 'vs/platform/event/common/event'; -import { IMessageService } from 'vs/platform/message/common/message'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { ITextModelResolverService } from 'vs/editor/common/services/resolverService'; import { RangeHighlightDecorations } from 'vs/workbench/common/editor/rangeDecorations'; +import { IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing'; // Ignore following contributions import { FoldingController } from 'vs/editor/contrib/folding/browser/folding'; @@ -65,8 +66,10 @@ export class DefaultPreferencesEditorInput extends ResourceEditorInput { private _willDispose = new Emitter(); public willDispose: Event = this._willDispose.event; - constructor(resource: URI, @ITextModelResolverService textModelResolverService: ITextModelResolverService) { - super(nls.localize('settingsEditorName', "Default Settings"), '', resource, textModelResolverService); + constructor(defaultSettingsResource: URI, + @ITextModelResolverService textModelResolverService: ITextModelResolverService + ) { + super(nls.localize('settingsEditorName', "Default Settings"), '', defaultSettingsResource, textModelResolverService); } getResource(): URI { @@ -182,8 +185,13 @@ export class DefaultPreferencesEditor extends BaseTextEditor { private updateInput(): TPromise { return this.input.resolve() - .then(editorModel => editorModel.load()) - .then(editorModel => this.getControl().setModel((editorModel).textEditorModel)); + .then(editorModel => TPromise.join([ + editorModel.load(), + // Default preferences editor is always part of side by side editor hence getting the master preferences model from active editor + // TODO:@sandy check with Ben + this.preferencesService.resolvePreferencesEditorModel(asFileEditorInput(this.editorService.getActiveEditorInput(), true).getResource()) + ])) + .then(([editorModel, preferencesModel]) => (this.getControl()).setModels((editorModel).textEditorModel, preferencesModel)); } private filterPreferences(filter: string) { @@ -205,7 +213,7 @@ export class DefaultPreferencesEditor extends BaseTextEditor { } private getDefaultPreferencesContribution(): PreferencesEditorContribution { - return (this.getControl()).getContribution(PreferencesEditorContribution.ID); + return (this.getControl()).getContribution(DefaultSettingsEditorContribution.ID); } protected restoreViewState(input: EditorInput) { @@ -244,6 +252,8 @@ export class DefaultPreferencesEditor extends BaseTextEditor { class DefaultPreferencesCodeEditor extends CodeEditor { + private _settingsModel: SettingsEditorModel; + protected _getContributions(): IEditorContributionCtor[] { let contributions = super._getContributions(); let skipContributions = [FoldingController.prototype, SelectionHighlighter.prototype, FindController.prototype]; @@ -251,16 +261,25 @@ class DefaultPreferencesCodeEditor extends CodeEditor { contributions.push(DefaultSettingsEditorContribution); return contributions; } + + setModels(model: editorCommon.IModel, settingsModel: SettingsEditorModel): void { + this._settingsModel = settingsModel; + return super.setModel(model); + } + + get settingsModel(): SettingsEditorModel { + return this._settingsModel; + } } export interface IPreferencesRenderer { render(); + updatePreference(setting: ISetting, value: any): void; dispose(); } export abstract class PreferencesEditorContribution extends Disposable implements editorCommon.IEditorContribution { - static ID: string = 'editor.contrib.preferences'; private preferencesRenderer: IPreferencesRenderer; constructor(protected editor: ICodeEditor, @@ -287,15 +306,12 @@ export abstract class PreferencesEditorContribution extends Disposable implement } } - getId(): string { - return PreferencesEditorContribution.ID; - } - getPreferencesRenderer(): IPreferencesRenderer { return this.preferencesRenderer; } protected abstract createPreferencesRenderer(editorModel: IPreferencesEditorModel): IPreferencesRenderer + abstract getId(): string; private disposePreferencesRenderer() { if (this.preferencesRenderer) { @@ -311,16 +327,30 @@ export abstract class PreferencesEditorContribution extends Disposable implement } export class DefaultSettingsEditorContribution extends PreferencesEditorContribution implements editorCommon.IEditorContribution { + + static ID: string = 'editor.contrib.defaultsettings'; + protected createPreferencesRenderer(editorModel: IPreferencesEditorModel): IPreferencesRenderer { if (editorModel instanceof DefaultSettingsEditorModel) { - return this.instantiationService.createInstance(DefaultSettingsRenderer, this.editor, editorModel); + return this.instantiationService.createInstance(DefaultSettingsRenderer, this.editor, editorModel, (this.editor).settingsModel); } return null; } + + getId(): string { + return DefaultSettingsEditorContribution.ID; + } } @editorContribution export class SettingsEditorContribution extends PreferencesEditorContribution implements editorCommon.IEditorContribution { + + static ID: string = 'editor.contrib.settings'; + + getId(): string { + return SettingsEditorContribution.ID; + } + protected createPreferencesRenderer(editorModel: IPreferencesEditorModel): IPreferencesRenderer { if (editorModel instanceof SettingsEditorModel) { return this.instantiationService.createInstance(SettingsRenderer, this.editor, editorModel); @@ -331,20 +361,35 @@ export class SettingsEditorContribution extends PreferencesEditorContribution im export class SettingsRenderer extends Disposable implements IPreferencesRenderer { - private copySettingActionRenderer: CopySettingRenderer; + private initializationPromise: TPromise; + private settingHighlighter: SettingHighlighter; + private editSettingActionRenderer: EditSettingRenderer; private modelChangeDelayer: Delayer = new Delayer(200); constructor(protected editor: ICodeEditor, protected settingsEditorModel: SettingsEditorModel, @IPreferencesService protected preferencesService: IPreferencesService, + @ITelemetryService private telemetryService: ITelemetryService, + @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, + @IMessageService private messageService: IMessageService, @IInstantiationService protected instantiationService: IInstantiationService ) { super(); - this.copySettingActionRenderer = this._register(instantiationService.createInstance(CopySettingRenderer, editor, false)); - this._register(editor.getModel().onDidChangeContent(() => this.modelChangeDelayer.trigger(() => this.onModelChanged()))); + this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor)); + this.initializationPromise = this.initialize(); } public render(): void { - this.copySettingActionRenderer.render(this.settingsEditorModel.settingsGroups); + this.initializationPromise.then(() => this.editSettingActionRenderer.render(this.settingsEditorModel.settingsGroups)); + } + + private initialize(): TPromise { + return this.preferencesService.createDefaultPreferencesEditorModel(this.preferencesService.defaultSettingsResource) + .then(defaultSettingsModel => { + this.editSettingActionRenderer = this._register(this.instantiationService.createInstance(EditSettingRenderer, this.editor, this.settingsEditorModel, defaultSettingsModel, this.settingHighlighter)); + this._register(this.editor.getModel().onDidChangeContent(() => this.modelChangeDelayer.trigger(() => this.onModelChanged()))); + this._register(this.editSettingActionRenderer.onUpdateSetting(({setting, value}) => this.updatePreference(setting, value))); + return null; + }); } private onModelChanged(): void { @@ -354,39 +399,58 @@ export class SettingsRenderer extends Disposable implements IPreferencesRenderer } this.render(); } + + public updatePreference(setting: ISetting, value: any): void { + this.telemetryService.publicLog('defaultSettingsActions.copySetting', { userConfigurationKeys: [setting.key] }); + this.configurationEditingService.writeConfiguration(this.settingsEditorModel.configurationTarget, { key: setting.key, value }, { writeToBuffer: true, autoSave: true }) + .then(() => this.onSettingUpdated(setting), error => this.messageService.show(Severity.Error, error)); + } + + private onSettingUpdated(setting: ISetting) { + this.editor.focus(); + setting = this.settingsEditorModel.getSetting(setting.key); + // TODO:@sandy Selection range should be template range + this.editor.setSelection(setting.valueRange); + this.settingHighlighter.highlight(this.settingsEditorModel.getSetting(setting.key), false, true); + } } export class DefaultSettingsRenderer extends Disposable implements IPreferencesRenderer { private defaultSettingsEditorContextKey: IContextKey; + private settingHighlighter: SettingHighlighter; private settingsGroupTitleRenderer: SettingsGroupTitleRenderer; private filteredMatchesRenderer: FilteredMatchesRenderer; private focusNextSettingRenderer: FocusNextSettingRenderer; private hiddenAreasRenderer: HiddenAreasRenderer; - private copySettingActionRenderer: CopySettingRenderer; + private editSettingActionRenderer: EditSettingRenderer; private settingsCountWidget: SettingsCountWidget; - constructor(protected editor: ICodeEditor, protected settingsEditorModel: DefaultSettingsEditorModel, + constructor(protected editor: ICodeEditor, protected defaultSettingsEditorModel: DefaultSettingsEditorModel, + private settingsEditorModel: SettingsEditorModel, @IPreferencesService protected preferencesService: IPreferencesService, @IContextKeyService contextKeyService: IContextKeyService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @IInstantiationService protected instantiationService: IInstantiationService ) { super(); this.defaultSettingsEditorContextKey = CONTEXT_DEFAULT_SETTINGS_EDITOR.bindTo(contextKeyService); + this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor)); this.settingsGroupTitleRenderer = this._register(instantiationService.createInstance(SettingsGroupTitleRenderer, editor)); this.filteredMatchesRenderer = this._register(instantiationService.createInstance(FilteredMatchesRenderer, editor)); this.focusNextSettingRenderer = this._register(instantiationService.createInstance(FocusNextSettingRenderer, editor)); - this.copySettingActionRenderer = this._register(instantiationService.createInstance(CopySettingRenderer, editor, true)); - this.settingsCountWidget = this._register(instantiationService.createInstance(SettingsCountWidget, editor, this.getCount(settingsEditorModel.settingsGroups))); - const paranthesisHidingRenderer = this._register(instantiationService.createInstance(StaticContentHidingRenderer, editor, settingsEditorModel.settingsGroups)); + this.editSettingActionRenderer = this._register(instantiationService.createInstance(EditSettingRenderer, editor, defaultSettingsEditorModel, settingsEditorModel, this.settingHighlighter)); + this._register(this.editSettingActionRenderer.onUpdateSetting(({setting, value}) => this.updatePreference(setting, value))); + this.settingsCountWidget = this._register(instantiationService.createInstance(SettingsCountWidget, editor, this.getCount(defaultSettingsEditorModel.settingsGroups))); + const paranthesisHidingRenderer = this._register(instantiationService.createInstance(StaticContentHidingRenderer, editor, defaultSettingsEditorModel.settingsGroups)); this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, paranthesisHidingRenderer])); } public render() { this.defaultSettingsEditorContextKey.set(true); - this.settingsGroupTitleRenderer.render(this.settingsEditorModel.settingsGroups); - this.copySettingActionRenderer.render(this.settingsEditorModel.settingsGroups); + this.settingsGroupTitleRenderer.render(this.defaultSettingsEditorModel.settingsGroups); + this.editSettingActionRenderer.render(this.defaultSettingsEditorModel.settingsGroups); this.settingsCountWidget.render(); this.hiddenAreasRenderer.render(); this.focusNextSettingRenderer.render([]); @@ -394,7 +458,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR } public filterPreferences(filter: string) { - const filterResult = this.settingsEditorModel.filterSettings(filter); + const filterResult = this.defaultSettingsEditorModel.filterSettings(filter); this.filteredMatchesRenderer.render(filterResult); this.settingsGroupTitleRenderer.render(filterResult.filteredGroups); this.settingsCountWidget.show(this.getCount(filterResult.filteredGroups)); @@ -418,6 +482,23 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR this.settingsGroupTitleRenderer.collapseAll(); } + public updatePreference(setting: ISetting, value: any): void { + const settingsEditor = this.getEditableSettingsEditor(); + if (settingsEditor) { + settingsEditor.getContribution(SettingsEditorContribution.ID).getPreferencesRenderer().updatePreference(setting, value); + } + } + + private getEditableSettingsEditor(): editorCommon.ICommonCodeEditor { + return this.editorService.getVisibleEditors() + .filter(editor => { + if (editorCommon.isCommonCodeEditor(editor.getControl())) { + return (editor.getControl()).getModel().uri.fsPath === this.settingsEditorModel.uri.fsPath; + } + }) + .map(editor => editor.getControl())[0]; + } + private getCount(settingsGroups: ISettingsGroup[]): number { let count = 0; for (const group of settingsGroups) { @@ -754,84 +835,95 @@ export class FocusNextSettingRenderer extends Disposable { } } -export class CopySettingRenderer extends Disposable { +class EditSettingRenderer extends Disposable { - private copyPreferenceWidgetForCusorPosition: CopyPreferenceWidget; - private copyPreferenceWidgetForMouseMove: CopyPreferenceWidget; + private editPreferenceWidgetForCusorPosition: EditPreferenceWidget; + private editPreferenceWidgetForMouseMove: EditPreferenceWidget; private settingsGroups: ISettingsGroup[]; - private toggleCopyPreferenceWidgetOnMouseMoveDelayer: Delayer; + private toggleEditPreferencesForMouseMoveDelayer: Delayer; - private copyRangeHighlighter: RangeHighlightDecorations; + private _onUpdateSetting: Emitter<{ setting: ISetting, value: any }> = new Emitter<{ setting: ISetting, value: any }>(); + public readonly onUpdateSetting: Event<{ setting: ISetting, value: any }> = this._onUpdateSetting.event; - constructor(private editor: ICodeEditor, private isDefaultSettings: boolean, + constructor(private editor: ICodeEditor, private masterSettingsModel: ISettingsEditorModel, + private otherSettingsModel: ISettingsEditorModel, + private settingHighlighter: SettingHighlighter, @IPreferencesService private preferencesService: IPreferencesService, @IInstantiationService private instantiationService: IInstantiationService, @IContextMenuService private contextMenuService: IContextMenuService ) { super(); - this.copyRangeHighlighter = this._register(instantiationService.createInstance(RangeHighlightDecorations)); - this.copyPreferenceWidgetForCusorPosition = this._register(this.instantiationService.createInstance(CopyPreferenceWidget, editor)); - this.copyPreferenceWidgetForMouseMove = this._register(this.instantiationService.createInstance(CopyPreferenceWidget, editor)); - this.toggleCopyPreferenceWidgetOnMouseMoveDelayer = new Delayer(50); + this.editPreferenceWidgetForCusorPosition = this._register(this.instantiationService.createInstance(EditPreferenceWidget, editor)); + this.editPreferenceWidgetForMouseMove = this._register(this.instantiationService.createInstance(EditPreferenceWidget, editor)); + this.toggleEditPreferencesForMouseMoveDelayer = new Delayer(75); - this._register(this.copyPreferenceWidgetForCusorPosition.onClick(setting => this.onEditSettingClicked(this.copyPreferenceWidgetForCusorPosition))); - this._register(this.copyPreferenceWidgetForMouseMove.onClick(setting => this.onEditSettingClicked(this.copyPreferenceWidgetForMouseMove))); + this._register(this.editPreferenceWidgetForCusorPosition.onClick(setting => this.onEditSettingClicked(this.editPreferenceWidgetForCusorPosition))); + this._register(this.editPreferenceWidgetForMouseMove.onClick(setting => this.onEditSettingClicked(this.editPreferenceWidgetForMouseMove))); - this._register(this.copyPreferenceWidgetForCusorPosition.onMouseOver(setting => this.onMouseOver(this.copyPreferenceWidgetForCusorPosition))); - this._register(this.copyPreferenceWidgetForMouseMove.onMouseOver(setting => this.onMouseOver(this.copyPreferenceWidgetForMouseMove))); + this._register(this.editPreferenceWidgetForCusorPosition.onMouseOver(setting => this.onMouseOver(this.editPreferenceWidgetForCusorPosition))); + this._register(this.editPreferenceWidgetForMouseMove.onMouseOver(setting => this.onMouseOver(this.editPreferenceWidgetForMouseMove))); this._register(this.editor.onDidChangeCursorPosition(positionChangeEvent => this.onPositionChanged(positionChangeEvent))); this._register(this.editor.onMouseMove(mouseMoveEvent => this.onMouseMoved(mouseMoveEvent))); } public render(settingsGroups: ISettingsGroup[]): void { - this.copyPreferenceWidgetForCusorPosition.hide(); - this.copyPreferenceWidgetForMouseMove.hide(); + this.editPreferenceWidgetForCusorPosition.hide(); + this.editPreferenceWidgetForMouseMove.hide(); this.settingsGroups = settingsGroups; const settings = this.getSettings(this.editor.getPosition().lineNumber); if (settings.length) { - this.showCopyPreferencesWidget(this.copyPreferenceWidgetForCusorPosition, settings); + this.showEditPreferencesWidget(this.editPreferenceWidgetForCusorPosition, settings); } } + private isDefaultSettings(): boolean { + return this.masterSettingsModel instanceof DefaultSettingsEditorModel; + } + private onPositionChanged(positionChangeEvent: editorCommon.ICursorPositionChangedEvent) { - this.copyPreferenceWidgetForMouseMove.hide(); + this.editPreferenceWidgetForMouseMove.hide(); const settings = this.getSettings(positionChangeEvent.position.lineNumber); if (settings.length) { - this.showCopyPreferencesWidget(this.copyPreferenceWidgetForCusorPosition, settings); + this.showEditPreferencesWidget(this.editPreferenceWidgetForCusorPosition, settings); } else { - this.copyPreferenceWidgetForCusorPosition.hide(); + this.editPreferenceWidgetForCusorPosition.hide(); } } private onMouseMoved(mouseMoveEvent: IEditorMouseEvent): void { - if (mouseMoveEvent.event.target === this.copyPreferenceWidgetForMouseMove.getDomNode() || - mouseMoveEvent.event.target === this.copyPreferenceWidgetForCusorPosition.getDomNode() + if (mouseMoveEvent.event.target === this.editPreferenceWidgetForMouseMove.getDomNode() || + mouseMoveEvent.event.target === this.editPreferenceWidgetForCusorPosition.getDomNode() ) { + this.onMouseOver(this.editPreferenceWidgetForMouseMove); return; } - this.copyRangeHighlighter.removeHighlightRange(); + this.settingHighlighter.clear(); + this.toggleEditPreferencesForMouseMoveDelayer.trigger(() => this.toggleEidtPreferenceWidgetForMouseMove(mouseMoveEvent)); + } + + private toggleEidtPreferenceWidgetForMouseMove(mouseMoveEvent: IEditorMouseEvent): void { const settings = mouseMoveEvent.target.position ? this.getSettings(mouseMoveEvent.target.position.lineNumber) : null; - if (settings && settings.length && mouseMoveEvent.target.position.lineNumber !== this.copyPreferenceWidgetForCusorPosition.getLine()) { - this.showCopyPreferencesWidget(this.copyPreferenceWidgetForMouseMove, settings); + if (settings && settings.length) { + this.showEditPreferencesWidget(this.editPreferenceWidgetForMouseMove, settings); } else { - this.copyPreferenceWidgetForMouseMove.hide(); + this.editPreferenceWidgetForMouseMove.hide(); } } - private showCopyPreferencesWidget(copyPreferencesWidget: CopyPreferenceWidget, settings: ISetting[]) { - copyPreferencesWidget.show(settings[0].valueRange.startLineNumber, settings); - copyPreferencesWidget.getDomNode().title = this.isDefaultSettings ? nls.localize('copyTitle', "Copy to settings") : nls.localize('updateTitle', "Update"); + private showEditPreferencesWidget(editPreferencesWidget: EditPreferenceWidget, settings: ISetting[]) { + editPreferencesWidget.show(settings[0].valueRange.startLineNumber, settings); + editPreferencesWidget.getDomNode().title = nls.localize('editTtile', "Edit"); } private getSettings(lineNumber: number): ISetting[] { const configurationMap = this.getConfigurationsMap(); return this.getSettingsAtLineNumber(lineNumber).filter(setting => { let jsonSchema: IJSONSchema = configurationMap[setting.key]; - return jsonSchema && (this.isDefaultSettings || jsonSchema.type === 'boolean' || jsonSchema.enum); + return jsonSchema && (this.isDefaultSettings() || jsonSchema.type === 'boolean' || jsonSchema.enum); }); } @@ -857,18 +949,15 @@ export class CopySettingRenderer extends Disposable { return settings; } - private onMouseOver(copyPreferenceWidget: CopyPreferenceWidget): void { - this.copyRangeHighlighter.highlightRange({ - resource: this.editor.getModel().uri, - range: copyPreferenceWidget.preferences[0].valueRange - }, this.editor); + private onMouseOver(editPreferenceWidget: EditPreferenceWidget): void { + this.settingHighlighter.highlight(editPreferenceWidget.preferences[0]); } - private onEditSettingClicked(copyPreferenceWidget: CopyPreferenceWidget): void { - const elementPosition = DOM.getDomNodePagePosition(copyPreferenceWidget.getDomNode()); + private onEditSettingClicked(editPreferenceWidget: EditPreferenceWidget): void { + const elementPosition = DOM.getDomNodePagePosition(editPreferenceWidget.getDomNode()); const anchor = { x: elementPosition.left + elementPosition.width, y: elementPosition.top + elementPosition.height + 10 }; - const actions = copyPreferenceWidget.preferences.length === 1 ? this.getActions(copyPreferenceWidget.preferences[0], this.getConfigurationsMap()[copyPreferenceWidget.preferences[0].key]) - : copyPreferenceWidget.preferences.map(setting => new ContextSubMenu(setting.key, this.getActions(setting, this.getConfigurationsMap()[setting.key]))); + const actions = editPreferenceWidget.preferences.length === 1 ? this.getActions(editPreferenceWidget.preferences[0], this.getConfigurationsMap()[editPreferenceWidget.preferences[0].key]) + : editPreferenceWidget.preferences.map(setting => new ContextSubMenu(setting.key, this.getActions(setting, this.getConfigurationsMap()[setting.key]))); this.contextMenuService.showContextMenu({ getAnchor: () => anchor, getActions: () => TPromise.wrap(actions) @@ -903,22 +992,56 @@ export class CopySettingRenderer extends Disposable { }; }); } - return [{ - id: 'copyToSettings', - label: nls.localize('copyToSettings', "Copy to Settings"), - enabled: true, - run: () => this.updateSetting(setting, setting.value) - }]; + return this.getDefaultActions(setting); + } + + private getDefaultActions(setting: ISetting): IAction[] { + const settingInOtherModel = this.otherSettingsModel.getSetting(setting.key); + if (this.isDefaultSettings()) { + return [{ + id: 'setDefaultValue', + label: settingInOtherModel ? nls.localize('replaceDefaultValue', "Replace in Settings") : nls.localize('copyDefaultValue', "Copy to Settings"), + enabled: true, + run: () => this.updateSetting(setting, setting.value) + }]; + } + return []; } private updateSetting(setting: ISetting, value: any): void { - this.copyRangeHighlighter.removeHighlightRange(); - this.preferencesService.updateSetting(setting, value); + this._onUpdateSetting.fire({ setting, value }); } } +class SettingHighlighter extends Disposable { -const DefaultSettingsEditorCommand = EditorCommand.bindToContribution((editor: editorCommon.ICommonCodeEditor) => editor.getContribution(PreferencesEditorContribution.ID)); + private fixedHighlighter: RangeHighlightDecorations; + private volatileHighlighter: RangeHighlightDecorations; + + constructor(private editor: editorCommon.ICommonCodeEditor, @IInstantiationService instantiationService: IInstantiationService) { + super(); + this.fixedHighlighter = this._register(instantiationService.createInstance(RangeHighlightDecorations)); + this.volatileHighlighter = this._register(instantiationService.createInstance(RangeHighlightDecorations)); + } + + highlight(setting: ISetting, isWholeLine: boolean = true, fix: boolean = false) { + this.volatileHighlighter.removeHighlightRange(); + this.fixedHighlighter.removeHighlightRange(); + + const highlighter = fix ? this.fixedHighlighter : this.volatileHighlighter; + highlighter.highlightRange({ + range: isWholeLine ? setting.valueRange : setting.range, + resource: this.editor.getModel().uri, + isWholeLine + }, this.editor); + } + + clear(): void { + this.volatileHighlighter.removeHighlightRange(); + } +} + +const DefaultSettingsEditorCommand = EditorCommand.bindToContribution((editor: editorCommon.ICommonCodeEditor) => editor.getContribution(DefaultSettingsEditorContribution.ID)); CommonEditorRegistry.registerEditorCommand(new DefaultSettingsEditorCommand({ id: DEFAULT_EDITOR_COMMAND_COLLAPSE_ALL, diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts index 8b14f5ce0d7..2f4815dc7cf 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -10,7 +10,7 @@ import URI from 'vs/base/common/uri'; import { LinkedMap as Map } from 'vs/base/common/map'; import * as labels from 'vs/base/common/labels'; import { Disposable } from 'vs/base/common/lifecycle'; -import { asFileEditorInput, SideBySideEditorInput, EditorInput } from 'vs/workbench/common/editor'; +import { SideBySideEditorInput, EditorInput } from 'vs/workbench/common/editor'; import { StringEditorInput } from 'vs/workbench/common/editor/stringEditorInput'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -23,14 +23,12 @@ import { IMessageService, Severity, IChoiceService } from 'vs/platform/message/c import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { IPreferencesService, IPreferencesEditorModel, ISetting } from 'vs/workbench/parts/preferences/common/preferences'; +import { IPreferencesService, IPreferencesEditorModel } from 'vs/workbench/parts/preferences/common/preferences'; import { SettingsEditorModel, DefaultSettingsEditorModel, DefaultKeybindingsEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { DefaultPreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; import { ITextModelResolverService } from 'vs/editor/common/services/resolverService'; -import { RangeHighlightDecorations } from 'vs/workbench/common/editor/rangeDecorations'; const SETTINGS_INFO_IGNORE_KEY = 'settings.workspace.info.ignore'; @@ -44,9 +42,6 @@ interface IWorkbenchSettingsConfiguration { export class PreferencesService extends Disposable implements IPreferencesService { - static DEFAULT_SETTINGS_URI: URI = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/settings.json' }); - static DEFAULT_KEY_BINDINGS_URI: URI = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/keybindings.json' }); - _serviceBrand: any; // TODO:@sandy merge these models into editor inputs by extending resource editor model @@ -54,8 +49,6 @@ export class PreferencesService extends Disposable implements IPreferencesServic private defaultSettingsEditorInputForUser: DefaultPreferencesEditorInput; private defaultSettingsEditorInputForWorkspace: DefaultPreferencesEditorInput; - private copyRangeHighlighter: RangeHighlightDecorations; - constructor( @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @IEditorGroupService private editorGroupService: IEditorGroupService, @@ -74,16 +67,18 @@ export class PreferencesService extends Disposable implements IPreferencesServic ) { super(); this.defaultPreferencesEditorModels = new Map(); - this.copyRangeHighlighter = this.instantiationService.createInstance(RangeHighlightDecorations); } + readonly defaultSettingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/settings.json' }); + readonly defaultKeybindingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/keybindings.json' }); + createDefaultPreferencesEditorModel(uri: URI): TPromise { const editorModel = this.defaultPreferencesEditorModels.get(uri); if (editorModel) { return TPromise.as(editorModel); } - if (PreferencesService.DEFAULT_SETTINGS_URI.fsPath === uri.fsPath) { + if (this.defaultSettingsResource.fsPath === uri.fsPath) { return TPromise.join([this.extensionService.onReady(), this.fetchMostCommonlyUsedSettings()]) .then(result => { const mostCommonSettings = result[1]; @@ -93,7 +88,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic }); } - if (PreferencesService.DEFAULT_KEY_BINDINGS_URI.fsPath === uri.fsPath) { + if (this.defaultKeybindingsResource.fsPath === uri.fsPath) { const model = this.instantiationService.createInstance(DefaultKeybindingsEditorModel, uri); this.defaultPreferencesEditorModels.set(uri, model); return TPromise.wrap(model); @@ -138,7 +133,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic openGlobalKeybindingSettings(): TPromise { const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to overwrite the defaults") + '\n[\n]'; - return this.createDefaultPreferencesEditorModel(PreferencesService.DEFAULT_KEY_BINDINGS_URI) + return this.createDefaultPreferencesEditorModel(this.defaultKeybindingsResource) .then(editorModel => { const defaultKeybindingsInput = this.instantiationService.createInstance(StringEditorInput, nls.localize('keybindingsEditorName', "Default Keyboard Shortcuts"), '', editorModel.content, 'json', true); this.openTwoEditors(defaultKeybindingsInput, URI.file(this.environmentService.appKeybindingsPath), emptyContents); @@ -154,31 +149,6 @@ export class PreferencesService extends Disposable implements IPreferencesServic })); } - public updateSetting(setting: ISetting, value: any): void { - const configurationTarget = this.getConfigurationTargetForCurrentActiveEditor(); - if (configurationTarget !== null) { - this.telemetryService.publicLog('defaultSettingsActions.copySetting', { userConfigurationKeys: [setting.key] }); - this.configurationEditingService.writeConfiguration(configurationTarget, { key: setting.key, value }, { writeToBuffer: true, autoSave: true }) - .then(() => this.onSettingUpdated(setting, configurationTarget), error => this.messageService.show(Severity.Error, error)); - } - } - - private onSettingUpdated(setting: ISetting, configurationTarget: ConfigurationTarget) { - const editorControl = this.editorService.getActiveEditor().getControl(); - editorControl.focus(); - this.resolvePreferencesEditorModel(this.getEditableSettingsURI(configurationTarget)) - .then(editorModel => { - const settingsEditorModel: SettingsEditorModel = editorModel; - setting = settingsEditorModel.getSetting(setting.key); - editorControl.setSelection(setting.valueRange); - this.copyRangeHighlighter.highlightRange({ - resource: editorModel.uri, - range: setting.range, - isWholeLine: false - }, editorControl); - }); - } - private resolveSettingsEditorModel(configurationTarget: ConfigurationTarget): TPromise { const settingsUri = this.getEditableSettingsURI(configurationTarget); if (settingsUri) { @@ -246,12 +216,12 @@ export class PreferencesService extends Disposable implements IPreferencesServic switch (configurationTarget) { case ConfigurationTarget.USER: if (!this.defaultSettingsEditorInputForUser) { - this.defaultSettingsEditorInputForUser = this._register(this.instantiationService.createInstance(DefaultPreferencesEditorInput, PreferencesService.DEFAULT_SETTINGS_URI)); + this.defaultSettingsEditorInputForUser = this._register(this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.defaultSettingsResource)); } return this.defaultSettingsEditorInputForUser; case ConfigurationTarget.WORKSPACE: if (!this.defaultSettingsEditorInputForWorkspace) { - this.defaultSettingsEditorInputForWorkspace = this._register(this.instantiationService.createInstance(DefaultPreferencesEditorInput, PreferencesService.DEFAULT_SETTINGS_URI)); + this.defaultSettingsEditorInputForWorkspace = this._register(this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.defaultSettingsResource)); } return this.defaultSettingsEditorInputForWorkspace; } @@ -279,28 +249,6 @@ export class PreferencesService extends Disposable implements IPreferencesServic }); } - private getConfigurationTargetForCurrentActiveEditor(): ConfigurationTarget { - const activeEditor = this.editorService.getActiveEditor(); - if (activeEditor) { - const editorInput = asFileEditorInput(activeEditor.input, true); - if (editorInput) { - return this.getConfigurationTarget(editorInput.getResource()); - } - } - return null; - } - - private getConfigurationTarget(resource: URI): ConfigurationTarget { - if (this.getEditableSettingsURI(ConfigurationTarget.USER).fsPath === resource.fsPath) { - return ConfigurationTarget.USER; - } - const workspaceSettingsUri = this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE); - if (workspaceSettingsUri && workspaceSettingsUri.fsPath === resource.fsPath) { - return ConfigurationTarget.WORKSPACE; - } - return null; - } - private fetchMostCommonlyUsedSettings(): TPromise { return TPromise.wrap([ 'editor.fontSize', diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts index 17343c40433..38a2dbcecc6 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -334,7 +334,7 @@ export class SettingsCountWidget extends Widget implements IOverlayWidget { } } -export class CopyPreferenceWidget extends Widget implements IOverlayWidget { +export class EditPreferenceWidget extends Widget implements IOverlayWidget { private static counter: number = 1; @@ -354,7 +354,7 @@ export class CopyPreferenceWidget extends Widget implements IOverlayWidget { @IContextMenuService contextMenuService: IContextMenuService ) { super(); - this._id = 'preferences.copyPreferenceWidget' + CopyPreferenceWidget.counter++; + this._id = 'preferences.editPreferenceWidget' + EditPreferenceWidget.counter++; this.editor.addOverlayWidget(this); this._register(this.editor.onDidScrollChange(() => { if (this._visible) { @@ -377,7 +377,7 @@ export class CopyPreferenceWidget extends Widget implements IOverlayWidget { this._domNode = document.createElement('div'); this._domNode.style.width = '20px'; this._domNode.style.height = '20px'; - this._domNode.className = 'copy-preferences-widget hidden'; + this._domNode.className = 'edit-preferences-widget hidden'; this.onclick(this._domNode, e => this._onClick.fire()); this.onmouseover(this._domNode, e => this._onMouseOver.fire()); } diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index 17ef49ad855..5f3f12b4bef 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -48,6 +48,7 @@ export interface IPreferencesEditorModel { export interface ISettingsEditorModel extends IPreferencesEditorModel { settingsGroups: ISettingsGroup[]; groupsTerms: string[]; + getSetting(key: string): ISetting; filterSettings(filter: string): IFilterResult; } @@ -59,14 +60,15 @@ export const IPreferencesService = createDecorator('prefere export interface IPreferencesService { _serviceBrand: any; + defaultSettingsResource: URI; + defaultKeybindingsResource: URI; + createDefaultPreferencesEditorModel(uri: URI): TPromise; resolvePreferencesEditorModel(uri: URI): TPromise; openGlobalSettings(): TPromise; openWorkspaceSettings(): TPromise; openGlobalKeybindingSettings(): TPromise; - - updateSetting(setting: ISetting, value: any): void; } export const CONTEXT_DEFAULT_SETTINGS_EDITOR = new RawContextKey('defaultSettingsEditor', false); From 23b329fdce47ff85a5a15ac18f85bdc0728df588 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 19 Dec 2016 18:10:34 +0100 Subject: [PATCH 003/131] Remove state management in Default preferences editor --- .../preferences/browser/preferencesEditor.ts | 41 +------------------ 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index f93e27a9366..e654e52c526 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -15,9 +15,8 @@ import { ArrayIterator } from 'vs/base/common/iterator'; import { IAction } from 'vs/base/common/actions'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import Event, { Emitter } from 'vs/base/common/event'; -import { LinkedMap as Map } from 'vs/base/common/map'; import { Registry } from 'vs/platform/platform'; -import { EditorOptions, EditorInput, asFileEditorInput } from 'vs/workbench/common/editor'; +import { EditorOptions, asFileEditorInput } from 'vs/workbench/common/editor'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; @@ -104,11 +103,8 @@ export class DefaultPreferencesEditorInput extends ResourceEditorInput { export class DefaultPreferencesEditor extends BaseTextEditor { public static ID: string = 'workbench.editor.defaultPreferences'; - private static VIEW_STATE: Map = new Map(); - private inputDisposeListener; private defaultSettingHeaderWidget: DefaultSettingsHeaderWidget; - private delayedFilterLogging: Delayer; constructor( @@ -161,9 +157,7 @@ export class DefaultPreferencesEditor extends BaseTextEditor { } setInput(input: DefaultPreferencesEditorInput, options: EditorOptions): TPromise { - this.listenToInput(input); - return super.setInput(input, options) - .then(() => this.updateInput()); + return super.setInput(input, options).then(() => this.updateInput()); } public layout(dimension: Dimension) { @@ -205,10 +199,6 @@ export class DefaultPreferencesEditor extends BaseTextEditor { public clearInput(): void { this.getControl().setModel(null); - this.saveState(this.input); - if (this.inputDisposeListener) { - this.inputDisposeListener.dispose(); - } super.clearInput(); } @@ -216,33 +206,6 @@ export class DefaultPreferencesEditor extends BaseTextEditor { return (this.getControl()).getContribution(DefaultSettingsEditorContribution.ID); } - protected restoreViewState(input: EditorInput) { - const viewState = DefaultPreferencesEditor.VIEW_STATE.get((input).getResource()); - if (viewState) { - this.getControl().restoreViewState(viewState); - } - } - - private saveState(input: DefaultPreferencesEditorInput) { - const state = this.getControl().saveViewState(); - if (state) { - const resource = input.getResource(); - if (DefaultPreferencesEditor.VIEW_STATE.has(resource)) { - DefaultPreferencesEditor.VIEW_STATE.delete(resource); - } - DefaultPreferencesEditor.VIEW_STATE.set(resource, state); - } - } - - private listenToInput(input: EditorInput) { - if (this.inputDisposeListener) { - this.inputDisposeListener.dispose(); - } - if (input instanceof DefaultPreferencesEditorInput) { - this.inputDisposeListener = (input).willDispose(() => this.saveState(input)); - } - } - private reportFilteringUsed(filter: string): void { let data = {}; data['filter'] = filter; From c0d61ce566a8c596be17e8d7f8cde930ccd91e31 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 20 Dec 2016 10:59:29 +0100 Subject: [PATCH 004/131] #17292 Let Filtered settings navigator use common setting highlighter to highlight setting --- .../preferences/browser/preferencesEditor.ts | 59 ++++++------------- 1 file changed, 17 insertions(+), 42 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index e654e52c526..aaa5c6e4a20 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -385,7 +385,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR private settingHighlighter: SettingHighlighter; private settingsGroupTitleRenderer: SettingsGroupTitleRenderer; private filteredMatchesRenderer: FilteredMatchesRenderer; - private focusNextSettingRenderer: FocusNextSettingRenderer; + private filteredSettingsNavigationRenderer: FilteredSettingsNavigationRenderer; private hiddenAreasRenderer: HiddenAreasRenderer; private editSettingActionRenderer: EditSettingRenderer; private settingsCountWidget: SettingsCountWidget; @@ -402,7 +402,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor)); this.settingsGroupTitleRenderer = this._register(instantiationService.createInstance(SettingsGroupTitleRenderer, editor)); this.filteredMatchesRenderer = this._register(instantiationService.createInstance(FilteredMatchesRenderer, editor)); - this.focusNextSettingRenderer = this._register(instantiationService.createInstance(FocusNextSettingRenderer, editor)); + this.filteredSettingsNavigationRenderer = this._register(instantiationService.createInstance(FilteredSettingsNavigationRenderer, editor, this.settingHighlighter)); this.editSettingActionRenderer = this._register(instantiationService.createInstance(EditSettingRenderer, editor, defaultSettingsEditorModel, settingsEditorModel, this.settingHighlighter)); this._register(this.editSettingActionRenderer.onUpdateSetting(({setting, value}) => this.updatePreference(setting, value))); this.settingsCountWidget = this._register(instantiationService.createInstance(SettingsCountWidget, editor, this.getCount(defaultSettingsEditorModel.settingsGroups))); @@ -416,7 +416,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR this.editSettingActionRenderer.render(this.defaultSettingsEditorModel.settingsGroups); this.settingsCountWidget.render(); this.hiddenAreasRenderer.render(); - this.focusNextSettingRenderer.render([]); + this.filteredSettingsNavigationRenderer.render([]); this.settingsGroupTitleRenderer.showGroup(1); } @@ -427,15 +427,15 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR this.settingsCountWidget.show(this.getCount(filterResult.filteredGroups)); if (!filter) { - this.focusNextSettingRenderer.render([]); + this.filteredSettingsNavigationRenderer.render([]); this.settingsGroupTitleRenderer.showGroup(1); } else { - this.focusNextSettingRenderer.render(filterResult.filteredGroups); + this.filteredSettingsNavigationRenderer.render(filterResult.filteredGroups); } } public focusNextSetting(): void { - const setting = this.focusNextSettingRenderer.focusNext(); + const setting = this.filteredSettingsNavigationRenderer.next(); if (setting) { this.settingsGroupTitleRenderer.showSetting(setting); } @@ -739,44 +739,25 @@ export class FilteredMatchesRenderer extends Disposable implements HiddenAreasPr } } -export class FocusNextSettingRenderer extends Disposable { +class FilteredSettingsNavigationRenderer extends Disposable { private iterator: ArrayIterator; - private decorationIds: string[] = []; - constructor(private editor: ICodeEditor) { + constructor(private editor: ICodeEditor, private settingHighlighter: SettingHighlighter) { super(); } - public focusNext(): ISetting { - this.clear(); + public next(): ISetting { let setting = this.iterator.next() || this.iterator.first(); if (setting) { - const model = this.editor.getModel(); - this.editor.changeDecorations(changeAccessor => { - this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, [{ - range: { - startLineNumber: setting.valueRange.startLineNumber, - startColumn: model.getLineMinColumn(setting.valueRange.startLineNumber), - endLineNumber: setting.valueRange.endLineNumber, - endColumn: model.getLineMaxColumn(setting.valueRange.endLineNumber) - }, - options: { - stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - className: 'rangeHighlight', - isWholeLine: true - } - }]); - }); - this.editor.revealLinesInCenterIfOutsideViewport(setting.valueRange.startLineNumber, setting.valueRange.endLineNumber - 1); + this.settingHighlighter.highlight(setting, true, true); return setting; } return null; } public render(filteredGroups: ISettingsGroup[]) { - this.clear(); - + this.settingHighlighter.clear(true); const settings: ISetting[] = []; for (const group of filteredGroups) { for (const section of group.sections) { @@ -785,17 +766,6 @@ export class FocusNextSettingRenderer extends Disposable { } this.iterator = new ArrayIterator(settings); } - - private clear() { - this.editor.changeDecorations(changeAccessor => { - this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, []); - }); - } - - public dispose() { - this.clear(); - super.dispose(); - } } class EditSettingRenderer extends Disposable { @@ -997,10 +967,15 @@ class SettingHighlighter extends Disposable { resource: this.editor.getModel().uri, isWholeLine }, this.editor); + + this.editor.revealLinesInCenterIfOutsideViewport(setting.valueRange.startLineNumber, setting.valueRange.endLineNumber - 1); } - clear(): void { + clear(fix: boolean = false): void { this.volatileHighlighter.removeHighlightRange(); + if (fix) { + this.fixedHighlighter.removeHighlightRange(); + } } } From 36b527159fda812b4432410977bdede21efc55ef Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 20 Dec 2016 12:05:42 +0100 Subject: [PATCH 005/131] Implement #16580 --- .../configuration/node/configurationEditingService.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/configuration/node/configurationEditingService.ts b/src/vs/workbench/services/configuration/node/configurationEditingService.ts index 3b68c03d4c9..2fb4383d637 100644 --- a/src/vs/workbench/services/configuration/node/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/node/configurationEditingService.ts @@ -68,7 +68,8 @@ export class ConfigurationEditingService implements IConfigurationEditingService // First validate before making any edits return this.validate(target, operation, options).then(validation => { - if (typeof validation.error === 'number') { + if (!options.writeToBuffer && typeof validation.error === 'number') { + // Target cannot contain JSON errors if writing to disk return this.wrapError(validation.error, target); } @@ -228,14 +229,14 @@ export class ConfigurationEditingService implements IConfigurationEditingService return { exists, contents: content }; } - // Target cannot contain JSON errors + let error = void 0; const parseErrors: json.ParseError[] = []; json.parse(content, parseErrors); if (parseErrors.length > 0) { - return { error: ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION }; + error = ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION; } - return { exists, contents: content }; + return { exists, contents: content, error }; }); }); } From 0a20ce4d081163ab2cdfe4b9cd6c6e788a13ecd6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 20 Dec 2016 14:13:17 +0100 Subject: [PATCH 006/131] #17292 - Do not bail out if settings has errors (Ignore invalid settings) - Fix mouse over on edit icon --- .../workbench/common/editor/rangeDecorations.ts | 3 ++- .../preferences/browser/preferencesEditor.ts | 17 +++++++++++++---- .../preferences/common/preferencesModels.ts | 4 ++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/common/editor/rangeDecorations.ts b/src/vs/workbench/common/editor/rangeDecorations.ts index 6104e528281..b03fca8e56e 100644 --- a/src/vs/workbench/common/editor/rangeDecorations.ts +++ b/src/vs/workbench/common/editor/rangeDecorations.ts @@ -62,7 +62,8 @@ export class RangeHighlightDecorations implements IDisposable { this.editor = editor; this.editorDisposables.push(this.editor.onDidChangeCursorPosition((e: editorCommon.ICursorPositionChangedEvent) => { if ( - e.reason === editorCommon.CursorChangeReason.Explicit + e.reason === editorCommon.CursorChangeReason.NotSet + || e.reason === editorCommon.CursorChangeReason.Explicit || e.reason === editorCommon.CursorChangeReason.Undo || e.reason === editorCommon.CursorChangeReason.Redo ) { diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index aaa5c6e4a20..e4a75b3adcf 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -828,16 +828,25 @@ class EditSettingRenderer extends Disposable { } private onMouseMoved(mouseMoveEvent: IEditorMouseEvent): void { - if (mouseMoveEvent.event.target === this.editPreferenceWidgetForMouseMove.getDomNode() || - mouseMoveEvent.event.target === this.editPreferenceWidgetForCusorPosition.getDomNode() - ) { - this.onMouseOver(this.editPreferenceWidgetForMouseMove); + const editPreferenceWidget = this.getEditPreferenceWidgetUnderMouse(mouseMoveEvent); + if (editPreferenceWidget) { + this.onMouseOver(editPreferenceWidget); return; } this.settingHighlighter.clear(); this.toggleEditPreferencesForMouseMoveDelayer.trigger(() => this.toggleEidtPreferenceWidgetForMouseMove(mouseMoveEvent)); } + private getEditPreferenceWidgetUnderMouse(mouseMoveEvent: IEditorMouseEvent): EditPreferenceWidget { + if (mouseMoveEvent.event.target === this.editPreferenceWidgetForMouseMove.getDomNode()) { + return this.editPreferenceWidgetForMouseMove; + } + if (mouseMoveEvent.event.target === this.editPreferenceWidgetForCusorPosition.getDomNode()) { + return this.editPreferenceWidgetForCusorPosition; + } + return null; + } + private toggleEidtPreferenceWidgetForMouseMove(mouseMoveEvent: IEditorMouseEvent): void { const settings = mouseMoveEvent.target.position ? this.getSettings(mouseMoveEvent.target.position.lineNumber) : null; if (settings && settings.length) { diff --git a/src/vs/workbench/parts/preferences/common/preferencesModels.ts b/src/vs/workbench/parts/preferences/common/preferencesModels.ts index a06097b1ea8..ecbb54549be 100644 --- a/src/vs/workbench/parts/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/parts/preferences/common/preferencesModels.ts @@ -261,6 +261,10 @@ export class SettingsEditorModel extends AbstractSettingsModel implements ISetti }, onLiteralValue: onValue, onError: (error) => { + const setting = settings[settings.length - 1]; + if (!setting.range || !setting.keyRange || !setting.valueRange) { + settings.pop(); + } } }; visit(model.getValue(), visitor); From 76f80ab06b9c317fc620b203983741a0fbfbdbfb Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 21 Dec 2016 08:23:39 +0100 Subject: [PATCH 007/131] #17646 Refactoring settings model to make it easy for search --- .../preferences/browser/preferencesEditor.ts | 8 +- .../parts/preferences/common/preferences.ts | 8 +- .../preferences/common/preferencesModels.ts | 117 ++++++++---------- 3 files changed, 63 insertions(+), 70 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index e4a75b3adcf..8d35c13e780 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -685,9 +685,9 @@ export class FilteredMatchesRenderer extends Disposable implements HiddenAreasPr }); } else { for (const section of group.sections) { - if (section.descriptionRange) { - if (!this.containsLine(section.descriptionRange.startLineNumber, filteredGroup)) { - notMatchesRanges.push(this.createCompleteRange(section.descriptionRange, model)); + if (section.titleRange) { + if (!this.containsLine(section.titleRange.startLineNumber, filteredGroup)) { + notMatchesRanges.push(this.createCompleteRange(section.titleRange, model)); } } for (const setting of section.settings) { @@ -707,7 +707,7 @@ export class FilteredMatchesRenderer extends Disposable implements HiddenAreasPr } for (const section of settingsGroup.sections) { - if (section.descriptionRange && lineNumber >= section.descriptionRange.startLineNumber && lineNumber <= section.descriptionRange.endLineNumber) { + if (section.titleRange && lineNumber >= section.titleRange.startLineNumber && lineNumber <= section.titleRange.endLineNumber) { return true; } diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index 5f3f12b4bef..2b3ed6940e5 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -19,8 +19,8 @@ export interface ISettingsGroup { } export interface ISettingsSection { - descriptionRange?: IRange; - description?: string; + titleRange?: IRange; + title?: string; settings: ISetting[]; } @@ -30,8 +30,8 @@ export interface ISetting { keyRange: IRange; value: any; valueRange: IRange; - description: string; - descriptionRange: IRange; + description: string[]; + descriptionRanges: IRange[]; } export interface IFilterResult { diff --git a/src/vs/workbench/parts/preferences/common/preferencesModels.ts b/src/vs/workbench/parts/preferences/common/preferencesModels.ts index ecbb54549be..e01c1dd977f 100644 --- a/src/vs/workbench/parts/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/parts/preferences/common/preferencesModels.ts @@ -16,11 +16,11 @@ import { IConfigurationNode, IConfigurationRegistry, Extensions } from 'vs/platf import { ISettingsEditorModel, IKeybindingsEditorModel, ISettingsGroup, ISetting, IFilterResult, ISettingsSection } from 'vs/workbench/parts/preferences/common/preferences'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { IFilter, IMatch, or, matchesContiguousSubString, matchesPrefix, matchesFuzzy, matchesWords } from 'vs/base/common/filters'; +import { IFilter, or, matchesContiguousSubString, matchesPrefix, /*matchesFuzzy,*/ matchesWords } from 'vs/base/common/filters'; export abstract class AbstractSettingsModel extends Disposable { - static _fuzzyFilter: IFilter = or(matchesPrefix, matchesContiguousSubString, matchesWords, matchesFuzzy); + static _descriptionFilter: IFilter = matchesWords; public get groupsTerms(): string[] { return this.settingsGroups.map(group => '@' + group.id); @@ -53,7 +53,7 @@ export abstract class AbstractSettingsModel extends Disposable { for (const section of group.sections) { const settings: ISetting[] = []; for (const setting of section.settings) { - const settingMatches = this._findMatchesInSetting(filter, regex, setting); + const settingMatches = this._findMatchesInSetting(filter, setting); if (groupMatched || settingMatches.length > 0) { settings.push(setting); } @@ -61,9 +61,9 @@ export abstract class AbstractSettingsModel extends Disposable { } if (settings.length) { sections.push({ - description: section.description, + title: section.title, settings, - descriptionRange: section.descriptionRange + titleRange: section.titleRange }); } } @@ -101,7 +101,38 @@ export abstract class AbstractSettingsModel extends Disposable { return null; } - protected abstract _findMatchesInSetting(searchString: string, searchRegex: RegExp, setting: ISetting): IRange[]; + private _findMatchesInSetting(searchString: string, setting: ISetting): IRange[] { + const result: IRange[] = [...this._findMatchesInDescription(searchString, setting)]; + result.push(...this._findMatchesInSettingKey(searchString, setting)); + return result; + } + + private _findMatchesInDescription(searchString: string, setting: ISetting): IRange[] { + const result: IRange[] = []; + for (var i = 0; i < setting.description.length; i++) { + var line = setting.description[i]; + const matches = matchesContiguousSubString(searchString, line) || []; + result.push(...matches.map(match => { + startLineNumber: setting.descriptionRanges[i].startLineNumber + i, + startColumn: setting.descriptionRanges[i].startColumn + match.start, + endLineNumber: setting.descriptionRanges[i].startLineNumber + i, + endColumn: setting.descriptionRanges[i].startColumn + match.end + })); + + } + return result; + } + + private _findMatchesInSettingKey(searchString: string, setting: ISetting): IRange[] { + const matches = or(matchesPrefix, matchesContiguousSubString, matchesWords)(searchString, setting.key) || []; + return matches.map(match => { + startLineNumber: setting.keyRange.startLineNumber, + startColumn: setting.keyRange.startColumn + match.start, + endLineNumber: setting.keyRange.startLineNumber, + endColumn: setting.keyRange.startColumn + match.end + }); + } + public abstract settingsGroups: ISettingsGroup[]; } @@ -196,7 +227,7 @@ export class SettingsEditorModel extends AbstractSettingsModel implements ISetti // setting started let settingStartPosition = model.getPositionAt(offset); settings.push({ - description: '', + description: [], key: name, keyRange: { startLineNumber: settingStartPosition.lineNumber, @@ -212,7 +243,7 @@ export class SettingsEditorModel extends AbstractSettingsModel implements ISetti }, value: null, valueRange: null, - descriptionRange: null, + descriptionRanges: null, }); } }, @@ -279,20 +310,6 @@ export class SettingsEditorModel extends AbstractSettingsModel implements ISetti range }] : []; } - - protected _findMatchesInSetting(searchString: string, searchRegex: RegExp, setting: ISetting): IRange[] { - const result: IRange[] = []; - for (let lineNumber = setting.range.startLineNumber; lineNumber <= setting.range.endLineNumber; lineNumber++) { - result.push(...this._findMatchesInLine(searchString, lineNumber)); - } - return result; - } - - private _findMatchesInLine(searchString: string, lineNumber: number): IRange[] { - return this.model.findMatches(searchString, { - startLineNumber: lineNumber, startColumn: this.model.getLineMinColumn(lineNumber), endLineNumber: lineNumber, endColumn: this.model.getLineMaxColumn(lineNumber), - }, false, false, false); - } } export class DefaultSettingsEditorModel extends AbstractSettingsModel implements ISettingsEditorModel { @@ -400,7 +417,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements result.push(settingsGroup); } } else { - settingsGroup.sections[settingsGroup.sections.length - 1].description = config.title; + settingsGroup.sections[settingsGroup.sections.length - 1].title = config.title; } } if (config.properties) { @@ -411,8 +428,8 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements const configurationSettings: ISetting[] = Object.keys(config.properties).map((key) => { const prop = config.properties[key]; const value = prop.default; - const description = prop.description || ''; - return { key, value, description, range: null, keyRange: null, valueRange: null, descriptionRange: null }; + const description = (prop.description || '').split('\n'); + return { key, value, description, range: null, keyRange: null, valueRange: null, descriptionRanges: [] }; }); settingsGroup.sections[settingsGroup.sections.length - 1].settings.push(...configurationSettings); } @@ -466,16 +483,20 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements this._contentByLines.push(''); let groupStart = this._contentByLines.length + 1; for (const section of group.sections) { - if (section.description) { + if (section.title) { let sectionTitleStart = this._contentByLines.length + 1; - this.addDescription(section.description, this.indent, this._contentByLines); - section.descriptionRange = { startLineNumber: sectionTitleStart, startColumn: 1, endLineNumber: this._contentByLines.length, endColumn: this._contentByLines[this._contentByLines.length - 1].length }; + this.addDescription([section.title], this.indent, this._contentByLines); + section.titleRange = { startLineNumber: sectionTitleStart, startColumn: 1, endLineNumber: this._contentByLines.length, endColumn: this._contentByLines[this._contentByLines.length - 1].length }; } for (const setting of section.settings) { const settingStart = this._contentByLines.length + 1; - this.addDescription(setting.description, this.indent, this._contentByLines); - setting.descriptionRange = { startLineNumber: settingStart, startColumn: 1, endLineNumber: this._contentByLines.length, endColumn: this._contentByLines[this._contentByLines.length - 1].length }; + setting.descriptionRanges = []; + const descriptionPreValue = this.indent + '// '; + for (const line of setting.description) { + this._contentByLines.push(descriptionPreValue + line); + setting.descriptionRanges.push({ startLineNumber: this._contentByLines.length, startColumn: this._contentByLines[this._contentByLines.length - 1].indexOf(line) + 1, endLineNumber: this._contentByLines.length, endColumn: this._contentByLines[this._contentByLines.length - 1].length }); + } let preValueConent = this.indent; const keyString = JSON.stringify(setting.key); @@ -506,41 +527,13 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements return lastSetting; } - private addDescription(description: string, indent: string, result: string[]) { - const multiLines = description.split('\n'); - for (const line of multiLines) { + private addDescription(description: string[], indent: string, result: string[]) { + for (const line of description) { result.push(indent + '// ' + line); } } - protected _findMatchesInSetting(searchString: string, searchRegex: RegExp, setting: ISetting): IRange[] { - const result: IRange[] = [...this._findMatchesInDescription(searchString, setting)]; - for (let lineNumber = setting.valueRange.startLineNumber; lineNumber <= setting.valueRange.endLineNumber; lineNumber++) { - result.push(...this._findMatchesInLine(searchRegex, lineNumber)); - } - return result; - } - - private _findMatchesInDescription(searchString: string, setting: ISetting): IRange[] { - const result: IRange[] = []; - for (let lineNumber = setting.descriptionRange.startLineNumber; lineNumber <= setting.descriptionRange.endLineNumber; lineNumber++) { - const content = this._contentByLines[lineNumber - 1]; - const matches: IMatch[] = AbstractSettingsModel._fuzzyFilter(searchString, content); - if (matches) { - result.push(...matches.map(match => { - return { - startLineNumber: lineNumber, - startColumn: match.start + 1, - endLineNumber: lineNumber, - endColumn: match.end + 1 - }; - })); - } - } - return result; - } - - private _findMatchesInLine(searchRegex: RegExp, lineNumber: number): IRange[] { + /*private _findMatchesInLine(searchRegex: RegExp, lineNumber: number): IRange[] { const result: IRange[] = []; const text = this._contentByLines[lineNumber - 1]; var m: RegExpExecArray; @@ -558,7 +551,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements } } while (m); return result; - } + }*/ } export class DefaultKeybindingsEditorModel implements IKeybindingsEditorModel { From ac22e18fa4b761b73efca565835717ea1a52781d Mon Sep 17 00:00:00 2001 From: Geir Sagberg Date: Mon, 26 Dec 2016 19:27:01 +0100 Subject: [PATCH 008/131] Check window.autoDetectHighContrast on window load --- src/vs/code/electron-main/window.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 5289884c526..d5c0da3e542 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -478,7 +478,7 @@ export class VSCodeWindow implements IVSCodeWindow { windowConfiguration.fullscreen = this._win.isFullScreen(); // Set Accessibility Config - windowConfiguration.highContrast = platform.isWindows && systemPreferences.isInvertedColorScheme(); + windowConfiguration.highContrast = platform.isWindows && systemPreferences.isInvertedColorScheme() && (!windowConfig || windowConfig.autoDetectHighContrast); windowConfiguration.accessibilitySupport = app.isAccessibilitySupportEnabled(); // Perf Counters From 20bbbfa6fee3ca00300e0632b79efd0054584749 Mon Sep 17 00:00:00 2001 From: Tilman Vatteroth Date: Tue, 27 Dec 2016 14:11:21 +0100 Subject: [PATCH 009/131] linux argument startup fix --- resources/linux/bin/code.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/linux/bin/code.sh b/resources/linux/bin/code.sh index 2bc6e635930..463a655a4d2 100755 --- a/resources/linux/bin/code.sh +++ b/resources/linux/bin/code.sh @@ -4,7 +4,6 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # If root, ensure that --user-data-dir is specified -ARGS=$@ if [ "$(id -u)" = "0" ]; then while test $# -gt 0 do @@ -34,5 +33,5 @@ fi ELECTRON="$VSCODE_PATH/@@NAME@@" CLI="$VSCODE_PATH/resources/app/out/cli.js" -ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" $ARGS +ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@" exit $? From 2d83288ef132b3fdfecd1598b8f20c695c4a4cd9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 28 Dec 2016 08:13:36 +0100 Subject: [PATCH 010/131] :lipstick: --- src/vs/workbench/browser/actionBarRegistry.ts | 2 +- .../browser/actions/configureLocale.ts | 2 +- src/vs/workbench/browser/composite.ts | 30 ++++---- src/vs/workbench/browser/panel.ts | 9 +-- src/vs/workbench/browser/part.ts | 36 +++------- .../workbench/browser/parts/compositePart.ts | 30 ++++---- .../browser/parts/editor/baseEditor.ts | 16 +---- .../browser/parts/editor/binaryDiffEditor.ts | 6 +- .../browser/parts/editor/binaryEditor.ts | 6 +- .../browser/parts/editor/editorPart.ts | 2 +- .../browser/parts/editor/sideBySideEditor.ts | 2 +- .../browser/parts/editor/stringEditor.ts | 14 ++-- .../browser/parts/editor/textDiffEditor.ts | 16 ++--- .../browser/parts/editor/textEditor.ts | 30 +------- .../browser/parts/panel/panelPart.ts | 4 +- .../browser/parts/sidebar/sidebarPart.ts | 2 +- .../browser/parts/statusbar/statusbar.ts | 4 +- .../browser/parts/statusbar/statusbarPart.ts | 46 ++++++------- src/vs/workbench/browser/viewlet.ts | 16 ++--- src/vs/workbench/common/component.ts | 44 ++++++------ .../common/editor/stringEditorInput.ts | 18 ----- src/vs/workbench/common/panel.ts | 1 + src/vs/workbench/common/viewlet.ts | 2 + .../electron-browser/workbench.main.ts | 6 +- .../workbench/electron-browser/workbench.ts | 2 +- .../cli/electron-browser/cli.contribution.ts | 4 +- .../files/browser/editors/binaryFileEditor.ts | 2 +- .../files/browser/editors/textFileEditor.ts | 29 ++++---- .../parts/files/browser/fileActions.ts | 68 ++++--------------- .../files.electron.contribution.ts | 6 +- .../output/browser/output.contribution.ts | 14 ++-- .../parts/output/browser/outputActions.ts | 1 + .../parts/output/browser/outputPanel.ts | 19 ++---- .../parts/output/browser/outputServices.ts | 1 + .../browser/preferences.contribution.ts | 6 +- .../preferences/browser/preferencesEditor.ts | 7 +- .../common/preferencesContentProvider.ts} | 8 +-- .../quickopen/browser/commandsHandler.ts | 2 +- .../browser/quickopen.contribution.ts | 29 ++++++-- .../browser/viewPickerHandler.ts | 0 .../search/browser/openAnythingHandler.ts | 2 - .../browser/viewpicker.contribution.ts | 33 --------- src/vs/workbench/test/browser/part.test.ts | 4 ++ .../browser/parts/editor/baseEditor.test.ts | 12 ++-- 44 files changed, 224 insertions(+), 369 deletions(-) rename src/vs/workbench/parts/{contentprovider/common/contentprovider.contribution.ts => preferences/common/preferencesContentProvider.ts} (87%) rename src/vs/workbench/parts/{viewpicker => quickopen}/browser/viewPickerHandler.ts (100%) delete mode 100644 src/vs/workbench/parts/viewpicker/browser/viewpicker.contribution.ts diff --git a/src/vs/workbench/browser/actionBarRegistry.ts b/src/vs/workbench/browser/actionBarRegistry.ts index a868dd8268c..146282f11e3 100644 --- a/src/vs/workbench/browser/actionBarRegistry.ts +++ b/src/vs/workbench/browser/actionBarRegistry.ts @@ -93,7 +93,7 @@ export class ContributableActionProvider implements IActionProvider { private registry: IActionBarRegistry; constructor() { - this.registry = (Registry.as(Extensions.Actionbar)); + this.registry = Registry.as(Extensions.Actionbar); } private toContext(tree: ITree, element: any): any { diff --git a/src/vs/workbench/browser/actions/configureLocale.ts b/src/vs/workbench/browser/actions/configureLocale.ts index d5b95c1b3f3..452cad675e1 100644 --- a/src/vs/workbench/browser/actions/configureLocale.ts +++ b/src/vs/workbench/browser/actions/configureLocale.ts @@ -89,5 +89,5 @@ const schema: IJSONSchema = } }; -const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); +const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); jsonRegistry.registerSchema(schemaId, schema); \ No newline at end of file diff --git a/src/vs/workbench/browser/composite.ts b/src/vs/workbench/browser/composite.ts index 6c2be7ccbf4..e18bb4cd531 100644 --- a/src/vs/workbench/browser/composite.ts +++ b/src/vs/workbench/browser/composite.ts @@ -46,14 +46,10 @@ export abstract class Composite extends WorkbenchComponent implements IComposite return null; } - public get telemetryService(): ITelemetryService { + protected get telemetryService(): ITelemetryService { return this._telemetryService; } - public get telemetryData(): any { - return this._telemetryData; - } - public get onTitleAreaUpdate(): Event { return this._onTitleAreaUpdate.event; } @@ -102,7 +98,7 @@ export abstract class Composite extends WorkbenchComponent implements IComposite // Only submit telemetry data when not running from an integration test if (this._telemetryService && this._telemetryService.publicLog) { - let eventName: string = 'compositeOpen'; + const eventName: string = 'compositeOpen'; this._telemetryService.publicLog(eventName, { composite: this.getId() }); } } @@ -114,7 +110,7 @@ export abstract class Composite extends WorkbenchComponent implements IComposite // Only submit telemetry data when not running from an integration test if (this._telemetryService && this._telemetryService.publicLog) { - let eventName: string = 'compositeShown'; + const eventName: string = 'compositeShown'; this._telemetryData.composite = this.getId(); this._telemetryService.publicLog(eventName, this._telemetryData); } @@ -223,10 +219,10 @@ export abstract class CompositeDescriptor extends AsyncDesc } export abstract class CompositeRegistry { - private composits: CompositeDescriptor[]; + private composites: CompositeDescriptor[]; constructor() { - this.composits = []; + this.composites = []; } protected registerComposite(descriptor: CompositeDescriptor): void { @@ -234,25 +230,25 @@ export abstract class CompositeRegistry { return; } - this.composits.push(descriptor); + this.composites.push(descriptor); } public getComposite(id: string): CompositeDescriptor { return this.compositeById(id); } - protected getComposits(): CompositeDescriptor[] { - return this.composits.slice(0); + protected getComposites(): CompositeDescriptor[] { + return this.composites.slice(0); } - protected setComposits(compositsToSet: CompositeDescriptor[]): void { - this.composits = compositsToSet; + protected setComposites(compositesToSet: CompositeDescriptor[]): void { + this.composites = compositesToSet; } private compositeById(id: string): CompositeDescriptor { - for (let i = 0; i < this.composits.length; i++) { - if (this.composits[i].id === id) { - return this.composits[i]; + for (let i = 0; i < this.composites.length; i++) { + if (this.composites[i].id === id) { + return this.composites[i]; } } diff --git a/src/vs/workbench/browser/panel.ts b/src/vs/workbench/browser/panel.ts index ce15a3dbc54..862686efabe 100644 --- a/src/vs/workbench/browser/panel.ts +++ b/src/vs/workbench/browser/panel.ts @@ -44,7 +44,7 @@ export class PanelRegistry extends CompositeRegistry { * Returns an array of registered panels known to the platform. */ public getPanels(): PanelDescriptor[] { - return this.getComposits(); + return this.getComposites(); } /** @@ -92,13 +92,14 @@ export abstract class TogglePanelAction extends Action { } private isPanelShowing(): boolean { - let panel = this.panelService.getActivePanel(); + const panel = this.panelService.getActivePanel(); + return panel && panel.getId() === this.panelId; } protected isPanelFocussed(): boolean { - let activePanel = this.panelService.getActivePanel(); - let activeElement = document.activeElement; + const activePanel = this.panelService.getActivePanel(); + const activeElement = document.activeElement; return activePanel && activeElement && DOM.isAncestor(activeElement, (activePanel).getContainer().getHTMLElement()); } diff --git a/src/vs/workbench/browser/part.ts b/src/vs/workbench/browser/part.ts index c59bc39484d..417066997c5 100644 --- a/src/vs/workbench/browser/part.ts +++ b/src/vs/workbench/browser/part.ts @@ -49,45 +49,31 @@ export abstract class Part extends WorkbenchComponent { /** * Subclasses override to provide a title area implementation. */ - public createTitleArea(parent: Builder): Builder { + protected createTitleArea(parent: Builder): Builder { return null; } - /** - * Returns the title area container. - */ - public getTitleArea(): Builder { - return this.titleArea; - } - /** * Subclasses override to provide a content area implementation. */ - public createContentArea(parent: Builder): Builder { + protected createContentArea(parent: Builder): Builder { return null; } /** * Returns the content area container. */ - public getContentArea(): Builder { + protected getContentArea(): Builder { return this.contentArea; } /** * Subclasses override to provide a status area implementation. */ - public createStatusArea(parent: Builder): Builder { + protected createStatusArea(parent: Builder): Builder { return null; } - /** - * Returns the status area container. - */ - public getStatusArea(): Builder { - return this.statusArea; - } - /** * Layout title, content and status area in the given dimension. */ @@ -138,7 +124,7 @@ export class PartLayout { } public computeStyle(): void { - let containerStyle = this.container.getComputedStyle(); + const containerStyle = this.container.getComputedStyle(); this.containerStyle = { borderLeftWidth: parseInt(containerStyle.getPropertyValue('border-left-width'), 10), borderRightWidth: parseInt(containerStyle.getPropertyValue('border-right-width'), 10), @@ -147,7 +133,7 @@ export class PartLayout { }; if (this.titleArea) { - let titleStyle = this.titleArea.getComputedStyle(); + const titleStyle = this.titleArea.getComputedStyle(); this.titleStyle = { display: titleStyle.getPropertyValue('display'), height: this.titleArea.getTotalSize().height @@ -155,7 +141,7 @@ export class PartLayout { } if (this.statusArea) { - let statusStyle = this.statusArea.getComputedStyle(); + const statusStyle = this.statusArea.getComputedStyle(); this.statusStyle = { display: statusStyle.getPropertyValue('display'), height: this.statusArea.getTotalSize().height @@ -168,11 +154,11 @@ export class PartLayout { this.computeStyle(); } - let width = dimension.width - (this.containerStyle.borderLeftWidth + this.containerStyle.borderRightWidth); - let height = dimension.height - (this.containerStyle.borderTopWidth + this.containerStyle.borderBottomWidth); + const width = dimension.width - (this.containerStyle.borderLeftWidth + this.containerStyle.borderRightWidth); + const height = dimension.height - (this.containerStyle.borderTopWidth + this.containerStyle.borderBottomWidth); // Return the applied sizes to title, content and status - let sizes: Dimension[] = []; + const sizes: Dimension[] = []; // Title Size: Width (Fill), Height (Variable) let titleSize: Dimension; @@ -192,7 +178,7 @@ export class PartLayout { } // Content Size: Width (Fill), Height (Variable) - let contentSize = new Dimension(width, height - titleSize.height - statusSize.height); + const contentSize = new Dimension(width, height - titleSize.height - statusSize.height); sizes.push(titleSize); sizes.push(contentSize); diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 13bb3934dd1..044e06f98c2 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -41,7 +41,7 @@ export abstract class CompositePart extends Part { private mapProgressServiceToComposite: { [compositeId: string]: IProgressService; }; private activeComposite: Composite; private lastActiveCompositeId: string; - private instantiatedComposits: Composite[]; + private instantiatedComposites: Composite[]; private titleLabel: Builder; private toolBar: ToolBar; private compositeLoaderPromises: { [compositeId: string]: TPromise; }; @@ -74,7 +74,7 @@ export abstract class CompositePart extends Part { this.mapActionsBindingToComposite = {}; this.mapProgressServiceToComposite = {}; this.activeComposite = null; - this.instantiatedComposits = []; + this.instantiatedComposites = []; this.compositeLoaderPromises = {}; } @@ -152,9 +152,9 @@ export abstract class CompositePart extends Part { protected createComposite(id: string, isActive?: boolean): TPromise { // Check if composite is already created - for (let i = 0; i < this.instantiatedComposits.length; i++) { - if (this.instantiatedComposits[i].getId() === id) { - return TPromise.as(this.instantiatedComposits[i]); + for (let i = 0; i < this.instantiatedComposites.length; i++) { + if (this.instantiatedComposites[i].getId() === id) { + return TPromise.as(this.instantiatedComposites[i]); } } @@ -170,7 +170,7 @@ export abstract class CompositePart extends Part { this.mapProgressServiceToComposite[composite.getId()] = progressService; // Remember as Instantiated - this.instantiatedComposits.push(composite); + this.instantiatedComposites.push(composite); // Register to title area update events from the composite this.instantiatedCompositeListeners.push(composite.onTitleAreaUpdate(() => this.onTitleAreaUpdate(composite.getId()))); @@ -181,7 +181,7 @@ export abstract class CompositePart extends Part { return composite; }); - // Report progress for slow loading composits + // Report progress for slow loading composites progressService.showWhile(loaderPromise, this.partService.isCreated() ? 800 : 3200 /* less ugly initial startup */); // Add to Promise Cache until Loaded @@ -207,7 +207,7 @@ export abstract class CompositePart extends Part { let createCompositePromise: TPromise; - // Composits created for the first time + // Composites created for the first time let compositeContainer = this.mapCompositeToCompositeContainer[composite.getId()]; if (!compositeContainer) { @@ -228,7 +228,7 @@ export abstract class CompositePart extends Part { createCompositePromise = TPromise.as(null); } - // Report progress for slow loading composits (but only if we did not create the composits before already) + // Report progress for slow loading composites (but only if we did not create the composites before already) let progressService = this.mapProgressServiceToComposite[composite.getId()]; if (progressService && !compositeContainer) { this.mapProgressServiceToComposite[composite.getId()].showWhile(createCompositePromise, this.partService.isCreated() ? 800 : 3200 /* less ugly initial startup */); @@ -351,7 +351,7 @@ export abstract class CompositePart extends Part { secondaryActions.push(...this.getSecondaryActions()); // From Contributions - let actionBarRegistry = Registry.as(Extensions.Actionbar); + let actionBarRegistry = Registry.as(Extensions.Actionbar); primaryActions.push(...actionBarRegistry.getActionBarActionsForContext(this.actionContributionScope, composite)); secondaryActions.push(...actionBarRegistry.getSecondaryActionBarActionsForContext(this.actionContributionScope, composite)); @@ -443,7 +443,7 @@ export abstract class CompositePart extends Part { // Check Registry if (!actionItem) { - let actionBarRegistry = Registry.as(Extensions.Actionbar); + let actionBarRegistry = Registry.as(Extensions.Actionbar); actionItem = actionBarRegistry.getActionItemForContext(this.actionContributionScope, ToolBarContext, action); } @@ -486,7 +486,7 @@ export abstract class CompositePart extends Part { } public shutdown(): void { - this.instantiatedComposits.forEach(i => i.shutdown()); + this.instantiatedComposites.forEach(i => i.shutdown()); super.shutdown(); } @@ -496,11 +496,11 @@ export abstract class CompositePart extends Part { this.mapProgressServiceToComposite = null; this.mapActionsBindingToComposite = null; - for (let i = 0; i < this.instantiatedComposits.length; i++) { - this.instantiatedComposits[i].dispose(); + for (let i = 0; i < this.instantiatedComposites.length; i++) { + this.instantiatedComposites[i].dispose(); } - this.instantiatedComposits = []; + this.instantiatedComposites = []; this.instantiatedCompositeListeners = dispose(this.instantiatedCompositeListeners); diff --git a/src/vs/workbench/browser/parts/editor/baseEditor.ts b/src/vs/workbench/browser/parts/editor/baseEditor.ts index aa27a69c0d2..1b584afd8df 100644 --- a/src/vs/workbench/browser/parts/editor/baseEditor.ts +++ b/src/vs/workbench/browser/parts/editor/baseEditor.ts @@ -30,7 +30,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; * This class is only intended to be subclassed and not instantiated. */ export abstract class BaseEditor extends Panel implements IEditor { - private _input: EditorInput; + protected _input: EditorInput; private _options: EditorOptions; private _position: Position; @@ -42,24 +42,10 @@ export abstract class BaseEditor extends Panel implements IEditor { return this._input; } - /** - * Returns the current input of this editor or null if none. - */ - public getInput(): EditorInput { - return this._input || null; - } - public get options(): EditorOptions { return this._options; } - /** - * Returns the current options of this editor or null if none. - */ - public getOptions(): EditorOptions { - return this._options || null; - } - /** * Note: Clients should not call this method, the workbench calls this * method. Calling it otherwise may result in unexpected behavior. diff --git a/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts b/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts index ac659ca6480..fef79c08e73 100644 --- a/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts @@ -56,7 +56,7 @@ export class BinaryResourceDiffEditor extends BaseEditor implements IVerticalSas } public getTitle(): string { - return this.getInput() ? this.getInput().getName() : nls.localize('binaryDiffEditor', "Binary Diff Viewer"); + return this.input ? this.input.getName() : nls.localize('binaryDiffEditor', "Binary Diff Viewer"); } public createEditor(parent: Builder): void { @@ -92,7 +92,7 @@ export class BinaryResourceDiffEditor extends BaseEditor implements IVerticalSas } public setInput(input: EditorInput, options?: EditorOptions): TPromise { - const oldInput = this.getInput(); + const oldInput = this.input; super.setInput(input, options); // Detect options @@ -112,7 +112,7 @@ export class BinaryResourceDiffEditor extends BaseEditor implements IVerticalSas } // Assert that the current input is still the one we expect. This prevents a race condition when loading a diff takes long and another input was set meanwhile - if (!this.getInput() || this.getInput() !== input) { + if (!this.input || this.input !== input) { return null; } diff --git a/src/vs/workbench/browser/parts/editor/binaryEditor.ts b/src/vs/workbench/browser/parts/editor/binaryEditor.ts index 4259d775fa2..e28966bcdeb 100644 --- a/src/vs/workbench/browser/parts/editor/binaryEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryEditor.ts @@ -41,7 +41,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor { } public getTitle(): string { - return this.getInput() ? this.getInput().getName() : nls.localize('binaryEditor', "Binary Viewer"); + return this.input ? this.input.getName() : nls.localize('binaryEditor', "Binary Viewer"); } public createEditor(parent: Builder): void { @@ -58,7 +58,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor { } public setInput(input: EditorInput, options?: EditorOptions): TPromise { - const oldInput = this.getInput(); + const oldInput = this.input; super.setInput(input, options); // Detect options @@ -78,7 +78,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor { } // Assert that the current input is still the one we expect. This prevents a race condition when loading takes long and another input was set meanwhile - if (!this.getInput() || this.getInput() !== input) { + if (!this.input || this.input !== input) { return null; } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 923c4a7f2c9..a087d318f48 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -437,7 +437,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService private doSetInput(group: EditorGroup, editor: BaseEditor, input: EditorInput, options: EditorOptions, monitor: ProgressMonitor): TPromise { // Emit Input-Changed Event as appropiate - const previousInput = editor.getInput(); + const previousInput = editor.input; const inputChanged = (!previousInput || !previousInput.matches(input) || (options && options.forceOpen)); // Call into Editor diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index 7bfbdfae87c..45f8f19f20b 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -46,7 +46,7 @@ export class SideBySideEditor extends BaseEditor { } public setInput(newInput: SideBySideEditorInput, options?: EditorOptions): TPromise { - const oldInput = this.getInput(); + const oldInput = this.input; return super.setInput(newInput, options) .then(() => this.updateInput(oldInput, newInput, options)); } diff --git a/src/vs/workbench/browser/parts/editor/stringEditor.ts b/src/vs/workbench/browser/parts/editor/stringEditor.ts index f68bb284d8b..be788f01aff 100644 --- a/src/vs/workbench/browser/parts/editor/stringEditor.ts +++ b/src/vs/workbench/browser/parts/editor/stringEditor.ts @@ -15,12 +15,9 @@ import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorIn import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import URI from 'vs/base/common/uri'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IStorageService } from 'vs/platform/storage/common/storage'; 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 { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IThemeService } from 'vs/workbench/services/themes/common/themeService'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -37,17 +34,14 @@ export class StringEditor extends BaseTextEditor { constructor( @ITelemetryService telemetryService: ITelemetryService, @IInstantiationService instantiationService: IInstantiationService, - @IWorkspaceContextService contextService: IWorkspaceContextService, @IStorageService storageService: IStorageService, - @IMessageService messageService: IMessageService, @IConfigurationService configurationService: IConfigurationService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IThemeService themeService: IThemeService, @IUntitledEditorService private untitledEditorService: IUntitledEditorService, @IEditorGroupService private editorGroupService: IEditorGroupService, @ITextFileService textFileService: ITextFileService ) { - super(StringEditor.ID, telemetryService, instantiationService, contextService, storageService, messageService, configurationService, editorService, themeService, textFileService); + super(StringEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService); this.toUnbind.push(this.untitledEditorService.onDidChangeDirty(e => this.onUntitledDirtyChange(e))); } @@ -67,7 +61,7 @@ export class StringEditor extends BaseTextEditor { } public setInput(input: EditorInput, options?: EditorOptions): TPromise { - const oldInput = this.getInput(); + const oldInput = this.input; super.setInput(input, options); // Detect options @@ -97,7 +91,7 @@ export class StringEditor extends BaseTextEditor { } // Assert that the current input is still the one we expect. This prevents a race condition when loading takes long and another input was set meanwhile - if (!this.getInput() || this.getInput() !== input) { + if (!this.input || this.input !== input) { return null; } @@ -135,7 +129,7 @@ export class StringEditor extends BaseTextEditor { protected getCodeEditorOptions(): IEditorOptions { const options = super.getCodeEditorOptions(); - const input = this.getInput(); + const input = this.input; const isUntitled = input instanceof UntitledEditorInput; const isReadonly = !isUntitled; // all string editors are readonly except for the untitled one diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 4a8496f3006..246a4d3948d 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -26,12 +26,10 @@ import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorMo import { DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IFileOperationResult, FileOperationResult } from 'vs/platform/files/common/files'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IMessageService } from 'vs/platform/message/common/message'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IThemeService } from 'vs/workbench/services/themes/common/themeService'; @@ -56,17 +54,15 @@ export class TextDiffEditor extends BaseTextEditor { constructor( @ITelemetryService telemetryService: ITelemetryService, @IInstantiationService instantiationService: IInstantiationService, - @IWorkspaceContextService contextService: IWorkspaceContextService, @IStorageService storageService: IStorageService, - @IMessageService messageService: IMessageService, @IConfigurationService configurationService: IConfigurationService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @IContextKeyService contextKeyService: IContextKeyService, @IThemeService themeService: IThemeService, @IEditorGroupService private editorGroupService: IEditorGroupService, @ITextFileService textFileService: ITextFileService ) { - super(TextDiffEditor.ID, telemetryService, instantiationService, contextService, storageService, messageService, configurationService, editorService, themeService, textFileService); + super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService); this.textDiffEditorVisible = TextCompareEditorVisible.bindTo(contextKeyService); } @@ -90,12 +86,12 @@ export class TextDiffEditor extends BaseTextEditor { // Check if arg4 is a position argument that differs from this editors position if (types.isUndefinedOrNull(arg3) || arg3 === false || arg3 === this.position) { - const activeDiffInput = this.getInput(); + const activeDiffInput = this.input; if (input && options && activeDiffInput) { // Input matches modified side of the diff editor: perform the action on modified side if (input.matches(activeDiffInput.modifiedInput)) { - return this.setInput(this.getInput(), options).then(() => this); + return this.setInput(this.input, options).then(() => this); } // Input matches original side of the diff editor: perform the action on original side @@ -120,7 +116,7 @@ export class TextDiffEditor extends BaseTextEditor { } public setInput(input: EditorInput, options?: EditorOptions): TPromise { - const oldInput = this.getInput(); + const oldInput = this.input; super.setInput(input, options); // Detect options @@ -152,7 +148,7 @@ export class TextDiffEditor extends BaseTextEditor { } // Assert that the current input is still the one we expect. This prevents a race condition when loading a diff takes long and another input was set meanwhile - if (!this.getInput() || this.getInput() !== input) { + if (!this.input || this.input !== input) { return null; } diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index ef22fe877b6..d9747208de9 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -15,15 +15,12 @@ import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorConfiguration } from 'vs/editor/common/config/commonEditorConfig'; import { IEditorViewState, IEditor, IEditorOptions, EventType as EditorEventType } from 'vs/editor/common/editorCommon'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IFilesConfiguration } from 'vs/platform/files/common/files'; import { Position } from 'vs/platform/editor/common/editor'; import { IStorageService } from 'vs/platform/storage/common/storage'; 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 { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IThemeService } from 'vs/workbench/services/themes/common/themeService'; import { ITextFileService, SaveReason, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles'; import { EventEmitter } from 'vs/base/common/eventEmitter'; @@ -51,11 +48,8 @@ export abstract class BaseTextEditor extends BaseEditor { id: string, @ITelemetryService telemetryService: ITelemetryService, @IInstantiationService private _instantiationService: IInstantiationService, - @IWorkspaceContextService private _contextService: IWorkspaceContextService, - @IStorageService private _storageService: IStorageService, - @IMessageService private _messageService: IMessageService, + @IStorageService private storageService: IStorageService, @IConfigurationService private configurationService: IConfigurationService, - @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, @IThemeService private themeService: IThemeService, @ITextFileService private textFileService: ITextFileService ) { @@ -65,30 +59,10 @@ export abstract class BaseTextEditor extends BaseEditor { this.toUnbind.push(themeService.onDidColorThemeChange(_ => this.handleConfigurationChangeEvent())); } - public get instantiationService(): IInstantiationService { + protected get instantiationService(): IInstantiationService { return this._instantiationService; } - public get contextService(): IWorkspaceContextService { - return this._contextService; - } - - public get storageService(): IStorageService { - return this._storageService; - } - - public get messageService() { - return this._messageService; - } - - public get editorService() { - return this._editorService; - } - - public get editorContainer(): Builder { - return this._editorContainer; - } - private handleConfigurationChangeEvent(configuration?: any): void { if (this.isVisible()) { this.applyConfiguration(configuration); diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 42eea7c819f..5d0395fd3f5 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -201,8 +201,8 @@ class ToggleMaximizedPanelAction extends Action { } } -let actionRegistry = Registry.as(WorkbenchExtensions.WorkbenchActions); +const actionRegistry = Registry.as(WorkbenchExtensions.WorkbenchActions); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TogglePanelAction, TogglePanelAction.ID, TogglePanelAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_J }), 'View: Toggle Panel Visibility', nls.localize('view', "View")); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPanelAction, FocusPanelAction.ID, FocusPanelAction.LABEL), 'View: Focus into Panel', nls.localize('view', "View")); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, ToggleMaximizedPanelAction.LABEL), 'View: Toggle Maximized Panel', nls.localize('view', "View")); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClosePanelAction, ClosePanelAction.ID, ClosePanelAction.LABEL), 'View: Close Panel', nls.localize('view', "View")); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClosePanelAction, ClosePanelAction.ID, ClosePanelAction.LABEL), 'View: Close Panel', nls.localize('view', "View")); \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 629f6f925dd..c381a4b44c2 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -141,7 +141,7 @@ class FocusSideBarAction extends Action { } } -let registry = Registry.as(ActionExtensions.WorkbenchActions); +const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(FocusSideBarAction, FocusSideBarAction.ID, FocusSideBarAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_0 }), 'View: Focus into Side Bar', nls.localize('viewCategory', "View")); diff --git a/src/vs/workbench/browser/parts/statusbar/statusbar.ts b/src/vs/workbench/browser/parts/statusbar/statusbar.ts index 431ca9bbbe0..6eb4f458930 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbar.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbar.ts @@ -6,9 +6,7 @@ import { Registry } from 'vs/platform/platform'; import { IDisposable } from 'vs/base/common/lifecycle'; -/* tslint:disable:no-unused-variable */ -import statusbarService = require('vs/platform/statusbar/common/statusbar'); -/* tslint:enable:no-unused-variable */ +import * as statusbarService from 'vs/platform/statusbar/common/statusbar'; import { SyncDescriptor0, createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index 3be9c056516..ac76450fd17 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -50,17 +50,17 @@ export class StatusbarPart extends Part implements IStatusbarService { public addEntry(entry: IStatusbarEntry, alignment: StatusbarAlignment, priority: number = 0): IDisposable { // Render entry in status bar - let el = this.doCreateStatusItem(alignment, priority); - let item = this.instantiationService.createInstance(StatusBarEntryItem, entry); - let toDispose = item.render(el); + const el = this.doCreateStatusItem(alignment, priority); + const item = this.instantiationService.createInstance(StatusBarEntryItem, entry); + const toDispose = item.render(el); // Insert according to priority - let container = this.statusItemsContainer.getHTMLElement(); - let neighbours = this.getEntries(alignment); + const container = this.statusItemsContainer.getHTMLElement(); + const neighbours = this.getEntries(alignment); let inserted = false; for (let i = 0; i < neighbours.length; i++) { - let neighbour = neighbours[i]; - let nPriority = $(neighbour).getProperty(StatusbarPart.PRIORITY_PROP); + const neighbour = neighbours[i]; + const nPriority = $(neighbour).getProperty(StatusbarPart.PRIORITY_PROP); if ( alignment === StatusbarAlignment.LEFT && nPriority < priority || alignment === StatusbarAlignment.RIGHT && nPriority > priority @@ -87,12 +87,12 @@ export class StatusbarPart extends Part implements IStatusbarService { } private getEntries(alignment: StatusbarAlignment): HTMLElement[] { - let entries: HTMLElement[] = []; + const entries: HTMLElement[] = []; - let container = this.statusItemsContainer.getHTMLElement(); - let children = container.children; + const container = this.statusItemsContainer.getHTMLElement(); + const children = container.children; for (let i = 0; i < children.length; i++) { - let childElement = children.item(i); + const childElement = children.item(i); if ($(childElement).getProperty(StatusbarPart.ALIGNMENT_PROP) === alignment) { entries.push(childElement); } @@ -105,18 +105,18 @@ export class StatusbarPart extends Part implements IStatusbarService { this.statusItemsContainer = $(parent); // Fill in initial items that were contributed from the registry - let registry = (Registry.as(Extensions.Statusbar)); + const registry = Registry.as(Extensions.Statusbar); - let leftDescriptors = registry.items.filter(d => d.alignment === StatusbarAlignment.LEFT).sort((a, b) => b.priority - a.priority); - let rightDescriptors = registry.items.filter(d => d.alignment === StatusbarAlignment.RIGHT).sort((a, b) => a.priority - b.priority); + const leftDescriptors = registry.items.filter(d => d.alignment === StatusbarAlignment.LEFT).sort((a, b) => b.priority - a.priority); + const rightDescriptors = registry.items.filter(d => d.alignment === StatusbarAlignment.RIGHT).sort((a, b) => a.priority - b.priority); - let descriptors = rightDescriptors.concat(leftDescriptors); // right first because they float + const descriptors = rightDescriptors.concat(leftDescriptors); // right first because they float this.toDispose.push(...descriptors.map(descriptor => { - let item = this.instantiationService.createInstance(descriptor.syncDescriptor); - let el = this.doCreateStatusItem(descriptor.alignment, descriptor.priority); + const item = this.instantiationService.createInstance(descriptor.syncDescriptor); + const el = this.doCreateStatusItem(descriptor.alignment, descriptor.priority); - let dispose = item.render(el); + const dispose = item.render(el); this.statusItemsContainer.append(el); return dispose; @@ -126,7 +126,7 @@ export class StatusbarPart extends Part implements IStatusbarService { } private doCreateStatusItem(alignment: StatusbarAlignment, priority: number = 0): HTMLElement { - let el = document.createElement('div'); + const el = document.createElement('div'); dom.addClass(el, 'statusbar-item'); if (alignment === StatusbarAlignment.RIGHT) { @@ -258,9 +258,9 @@ class StatusBarEntryItem implements IStatusbarItem { private executeCommand(id: string) { // Lookup built in commands - let builtInActionDescriptor = (Registry.as(ActionExtensions.WorkbenchActions)).getWorkbenchAction(id); + const builtInActionDescriptor = Registry.as(ActionExtensions.WorkbenchActions).getWorkbenchAction(id); if (builtInActionDescriptor) { - let action = this.instantiationService.createInstance(builtInActionDescriptor.syncDescriptor); + const action = this.instantiationService.createInstance(builtInActionDescriptor.syncDescriptor); if (action.enabled) { this.telemetryService.publicLog('workbenchActionExecuted', { id: action.id, from: 'status bar' }); @@ -275,8 +275,8 @@ class StatusBarEntryItem implements IStatusbarItem { } // Maintain old behaviour of always focusing the editor here - let activeEditor = this.editorService.getActiveEditor(); - let codeEditor = getCodeEditor(activeEditor); + const activeEditor = this.editorService.getActiveEditor(); + const codeEditor = getCodeEditor(activeEditor); if (codeEditor) { codeEditor.focus(); } diff --git a/src/vs/workbench/browser/viewlet.ts b/src/vs/workbench/browser/viewlet.ts index f39bf7369bc..eab5164b635 100644 --- a/src/vs/workbench/browser/viewlet.ts +++ b/src/vs/workbench/browser/viewlet.ts @@ -100,7 +100,7 @@ export abstract class ViewerViewlet extends Viewlet { } // Make sure the current selected element is revealed - let selection = this.viewer.getSelection(); + const selection = this.viewer.getSelection(); if (selection.length > 0) { this.reveal(selection[0], 0.5).done(null, errors.onUnexpectedError); } @@ -199,7 +199,7 @@ export class ViewletRegistry extends CompositeRegistry { * Returns an array of registered viewlets known to the platform. */ public getViewlets(): ViewletDescriptor[] { - return this.getComposits() as ViewletDescriptor[]; + return this.getComposites() as ViewletDescriptor[]; } /** @@ -246,7 +246,7 @@ export class ToggleViewletAction extends Action { } // Otherwise pass focus to editor if possible - let editor = this.editorService.getActiveEditor(); + const editor = this.editorService.getActiveEditor(); if (editor) { editor.focus(); } @@ -255,14 +255,14 @@ export class ToggleViewletAction extends Action { } private otherViewletShowing(): boolean { - let activeViewlet = this.viewletService.getActiveViewlet(); + const activeViewlet = this.viewletService.getActiveViewlet(); return !activeViewlet || activeViewlet.getId() !== this.viewletId; } private sidebarHasFocus(): boolean { - let activeViewlet = this.viewletService.getActiveViewlet(); - let activeElement = document.activeElement; + const activeViewlet = this.viewletService.getActiveViewlet(); + const activeElement = document.activeElement; return activeViewlet && activeElement && DOM.isAncestor(activeElement, (activeViewlet).getContainer().getHTMLElement()); } @@ -566,7 +566,7 @@ export abstract class CollapsibleViewletView extends CollapsibleView implements } function renderViewTree(container: HTMLElement): HTMLElement { - let treeContainer = document.createElement('div'); + const treeContainer = document.createElement('div'); container.appendChild(treeContainer); return treeContainer; @@ -596,7 +596,7 @@ function focus(tree: ITree): void { } // Make sure the current selected element is revealed - let selection = tree.getSelection(); + const selection = tree.getSelection(); if (selection.length > 0) { reveal(tree, selection[0], 0.5).done(null, errors.onUnexpectedError); } diff --git a/src/vs/workbench/common/component.ts b/src/vs/workbench/common/component.ts index 69955150e86..ebc1f02415a 100644 --- a/src/vs/workbench/common/component.ts +++ b/src/vs/workbench/common/component.ts @@ -20,26 +20,6 @@ export interface IWorkbenchComponent extends IDisposable { */ getId(): string; - /** - * Returns a JSON Object that represents the data of this memento. The optional - * parameter scope allows to specify the scope of the memento to load. If not - * provided, the scope will be global, Scope.WORKSPACE can be used to - * scope the memento to the workspace. - * - * Mementos are shared across components with the same id. This means that multiple components - * with the same id will store data into the same data structure. - */ - getMemento(storageService: IStorageService, scope?: Scope): any; - - /** - * Saves all data of the mementos that have been loaded to the local storage. This includes - * global and workspace scope. - * - * Mementos are shared across components with the same id. This means that multiple components - * with the same id will store data into the same data structure. - */ - saveMemento(): void; - /** * Called when the browser containing the container is closed. * @@ -61,12 +41,13 @@ export class WorkbenchComponent extends Disposable implements IWorkbenchComponen constructor(id: string) { super(); + this._toUnbind = []; this.id = id; this.componentMemento = new Memento(this.id); } - public get toUnbind() { + protected get toUnbind() { return this._toUnbind; } @@ -74,11 +55,27 @@ export class WorkbenchComponent extends Disposable implements IWorkbenchComponen return this.id; } - public getMemento(storageService: IStorageService, scope: Scope = Scope.GLOBAL): any { + /** + * Returns a JSON Object that represents the data of this memento. The optional + * parameter scope allows to specify the scope of the memento to load. If not + * provided, the scope will be global, Scope.WORKSPACE can be used to + * scope the memento to the workspace. + * + * Mementos are shared across components with the same id. This means that multiple components + * with the same id will store data into the same data structure. + */ + protected getMemento(storageService: IStorageService, scope: Scope = Scope.GLOBAL): any { return this.componentMemento.getMemento(storageService, scope); } - public saveMemento(): void { + /** + * Saves all data of the mementos that have been loaded to the local storage. This includes + * global and workspace scope. + * + * Mementos are shared across components with the same id. This means that multiple components + * with the same id will store data into the same data structure. + */ + protected saveMemento(): void { this.componentMemento.saveMemento(); } @@ -90,6 +87,7 @@ export class WorkbenchComponent extends Disposable implements IWorkbenchComponen public dispose(): void { this._toUnbind = dispose(this._toUnbind); + super.dispose(); } } \ No newline at end of file diff --git a/src/vs/workbench/common/editor/stringEditorInput.ts b/src/vs/workbench/common/editor/stringEditorInput.ts index 87447fb85d9..9e4d7656293 100644 --- a/src/vs/workbench/common/editor/stringEditorInput.ts +++ b/src/vs/workbench/common/editor/stringEditorInput.ts @@ -94,24 +94,6 @@ export class StringEditorInput extends EditorInput { } } - /** - * Removes all lines from the top if the line number exceeds the given line count. Returns the new value if lines got trimmed. - * - * Note: This method is a no-op if the input has not yet been resolved. - */ - public trim(linecount: number): string { - if (this.cachedModel) { - let newValue = this.cachedModel.trim(linecount); - if (newValue !== null) { - this.value = newValue; - - return this.value; - } - } - - return null; - } - public resolve(refresh?: boolean): TPromise { // Use Cached Model diff --git a/src/vs/workbench/common/panel.ts b/src/vs/workbench/common/panel.ts index 57c365b0a62..2ac319835f1 100644 --- a/src/vs/workbench/common/panel.ts +++ b/src/vs/workbench/common/panel.ts @@ -2,6 +2,7 @@ * 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 { IComposite } from 'vs/workbench/common/composite'; diff --git a/src/vs/workbench/common/viewlet.ts b/src/vs/workbench/common/viewlet.ts index 9ef11ebf92f..1d6b901f78e 100644 --- a/src/vs/workbench/common/viewlet.ts +++ b/src/vs/workbench/common/viewlet.ts @@ -2,10 +2,12 @@ * 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 { IComposite } from 'vs/workbench/common/composite'; export interface IViewlet extends IComposite { + /** * Returns the minimal width needed to avoid any content horizontal truncation */ diff --git a/src/vs/workbench/electron-browser/workbench.main.ts b/src/vs/workbench/electron-browser/workbench.main.ts index 00480dbd261..8ff9a3ddd07 100644 --- a/src/vs/workbench/electron-browser/workbench.main.ts +++ b/src/vs/workbench/electron-browser/workbench.main.ts @@ -86,8 +86,6 @@ import 'vs/workbench/parts/execution/electron-browser/terminal.contribution'; import 'vs/workbench/parts/snippets/electron-browser/snippets.contribution'; -import 'vs/workbench/parts/contentprovider/common/contentprovider.contribution'; - import 'vs/workbench/parts/themes/electron-browser/themes.contribution'; import 'vs/workbench/parts/feedback/electron-browser/feedback.contribution'; @@ -107,6 +105,4 @@ import 'vs/workbench/electron-browser/main'; import 'vs/workbench/parts/themes/test/electron-browser/themes.test.contribution'; -import 'vs/workbench/parts/watermark/electron-browser/watermark'; - -import 'vs/workbench/parts/viewpicker/browser/viewpicker.contribution'; +import 'vs/workbench/parts/watermark/electron-browser/watermark'; \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index a903d8590d3..d483cf7d956 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -262,7 +262,7 @@ export class Workbench implements IPartService { // Workbench Layout this.createWorkbenchLayout(); - // Load composits and editors in parallel + // Load composites and editors in parallel const compositeAndEditorPromises: TPromise[] = []; // Restore last opened viewlet diff --git a/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts b/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts index 2177ef468c9..43170f0f222 100644 --- a/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts +++ b/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts @@ -249,10 +249,10 @@ class DarwinCLIHelper implements IWorkbenchContribution { if (process.platform === 'darwin') { const category = nls.localize('shellCommand', "Shell Command"); - const workbenchActionsRegistry = Registry.as(ActionExtensions.WorkbenchActions); + const workbenchActionsRegistry = Registry.as(ActionExtensions.WorkbenchActions); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(InstallAction, InstallAction.ID, InstallAction.LABEL), 'Shell Command: Install \'code\' command in PATH', category); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(UninstallAction, UninstallAction.ID, UninstallAction.LABEL), 'Shell Command: Uninstall \'code\' command from PATH', category); - const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); + const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(DarwinCLIHelper); } diff --git a/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.ts b/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.ts index a8d93d2d011..1da3b242a48 100644 --- a/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.ts +++ b/src/vs/workbench/parts/files/browser/editors/binaryFileEditor.ts @@ -23,6 +23,6 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { } public getTitle(): string { - return this.getInput() ? this.getInput().getName() : nls.localize('binaryFileEditor', "Binary File Viewer"); + return this.input ? this.input.getName() : nls.localize('binaryFileEditor', "Binary File Viewer"); } } \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts index 0141bb01fe9..38f317eaaea 100644 --- a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts @@ -27,7 +27,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IMessageService, CancelAction } from 'vs/platform/message/common/message'; +import { CancelAction } from 'vs/platform/message/common/message'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IThemeService } from 'vs/workbench/services/themes/common/themeService'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; @@ -44,17 +44,16 @@ export class TextFileEditor extends BaseTextEditor { @IFileService private fileService: IFileService, @IViewletService private viewletService: IViewletService, @IInstantiationService instantiationService: IInstantiationService, - @IWorkspaceContextService contextService: IWorkspaceContextService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, @IStorageService storageService: IStorageService, @IHistoryService private historyService: IHistoryService, - @IMessageService messageService: IMessageService, @IConfigurationService configurationService: IConfigurationService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @IThemeService themeService: IThemeService, @IEditorGroupService private editorGroupService: IEditorGroupService, @ITextFileService textFileService: ITextFileService ) { - super(TextFileEditor.ID, telemetryService, instantiationService, contextService, storageService, messageService, configurationService, editorService, themeService, textFileService); + super(TextFileEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService); // Clear view state for deleted files this.toUnbind.push(this.fileService.onFileChanges(e => this.onFilesChanged(e))); @@ -68,15 +67,15 @@ export class TextFileEditor extends BaseTextEditor { } public getTitle(): string { - return this.getInput() ? this.getInput().getName() : nls.localize('textFileEditor', "Text File Editor"); + return this.input ? this.input.getName() : nls.localize('textFileEditor', "Text File Editor"); } - public getInput(): FileEditorInput { - return super.getInput(); + public get input(): FileEditorInput { + return this._input as FileEditorInput; } public setInput(input: FileEditorInput, options?: EditorOptions): TPromise { - const oldInput = this.getInput(); + const oldInput = this.input; super.setInput(input, options); // Detect options @@ -121,9 +120,9 @@ export class TextFileEditor extends BaseTextEditor { // Check Model state const textFileModel = resolvedModel; - const hasInput = !!this.getInput(); + const hasInput = !!this.input; const modelDisposed = textFileModel.isDisposed(); - const inputChanged = hasInput && this.getInput().getResource().toString() !== textFileModel.getResource().toString(); + const inputChanged = hasInput && this.input.getResource().toString() !== textFileModel.getResource().toString(); if ( !hasInput || // editor got hidden meanwhile modelDisposed || // input got disposed meanwhile @@ -137,7 +136,7 @@ export class TextFileEditor extends BaseTextEditor { textEditor.setModel(textFileModel.textEditorModel); // Always restore View State if any associated - const editorViewState = this.loadTextEditorViewState(this.getInput().getResource().toString()); + const editorViewState = this.loadTextEditorViewState(this.input.getResource().toString()); if (editorViewState) { textEditor.restoreViewState(editorViewState); } @@ -210,7 +209,7 @@ export class TextFileEditor extends BaseTextEditor { protected getCodeEditorOptions(): IEditorOptions { const options = super.getCodeEditorOptions(); - const input = this.getInput(); + const input = this.input; const inputName = input && input.getName(); let ariaLabel: string; @@ -237,7 +236,7 @@ export class TextFileEditor extends BaseTextEditor { // Keep editor view state in settings to restore when coming back if (this.input) { - this.saveTextEditorViewState(this.getInput().getResource().toString()); + this.saveTextEditorViewState(this.input.getResource().toString()); } // Clear Model @@ -251,7 +250,7 @@ export class TextFileEditor extends BaseTextEditor { // Save View State if (this.input) { - this.saveTextEditorViewState(this.getInput().getResource().toString()); + this.saveTextEditorViewState(this.input.getResource().toString()); } // Call Super diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts index 53ac3369199..9733f4c95de 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -65,8 +65,6 @@ export class BaseFileAction extends Action { constructor( id: string, label: string, - @IWorkspaceContextService private _contextService: IWorkspaceContextService, - @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, @IFileService private _fileService: IFileService, @IMessageService private _messageService: IMessageService, @ITextFileService private _textFileService: ITextFileService @@ -76,18 +74,10 @@ export class BaseFileAction extends Action { this.enabled = false; } - public get contextService() { - return this._contextService; - } - public get messageService() { return this._messageService; } - public get editorService() { - return this._editorService; - } - public get fileService() { return this._fileService; } @@ -109,7 +99,7 @@ export class BaseFileAction extends Action { } _updateEnablement(): void { - this.enabled = !!(this._contextService && this._fileService && this._editorService && this._isEnabled()); + this.enabled = !!(this._fileService && this._isEnabled()); } protected onError(error: any): void { @@ -149,14 +139,12 @@ export class TriggerRenameFileAction extends BaseFileAction { constructor( tree: ITree, element: FileStat, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService, @IInstantiationService instantiationService: IInstantiationService ) { - super(TriggerRenameFileAction.ID, nls.localize('rename', "Rename"), contextService, editorService, fileService, messageService, textFileService); + super(TriggerRenameFileAction.ID, nls.localize('rename', "Rename"), fileService, messageService, textFileService); this.tree = tree; this.element = element; @@ -220,13 +208,11 @@ export abstract class BaseRenameAction extends BaseFileAction { id: string, label: string, element: FileStat, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService ) { - super(id, label, contextService, editorService, fileService, messageService, textFileService); + super(id, label, fileService, messageService, textFileService); this.element = element; } @@ -283,13 +269,11 @@ class RenameFileAction extends BaseRenameAction { constructor( element: FileStat, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService ) { - super(RenameFileAction.ID, nls.localize('rename', "Rename"), element, contextService, editorService, fileService, messageService, textFileService); + super(RenameFileAction.ID, nls.localize('rename', "Rename"), element, fileService, messageService, textFileService); this._updateEnablement(); } @@ -347,13 +331,11 @@ export class BaseNewAction extends BaseFileAction { isFile: boolean, editableAction: BaseRenameAction, element: FileStat, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService ) { - super(id, label, contextService, editorService, fileService, messageService, textFileService); + super(id, label, fileService, messageService, textFileService); if (element) { this.presetFolder = element.isDirectory ? element : element.parent; @@ -437,14 +419,12 @@ export class NewFileAction extends BaseNewAction { constructor( tree: ITree, element: FileStat, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService, @IInstantiationService instantiationService: IInstantiationService ) { - super('workbench.action.files.newFile', nls.localize('newFile', "New File"), tree, true, instantiationService.createInstance(CreateFileAction, element), null, contextService, editorService, fileService, messageService, textFileService); + super('workbench.action.files.newFile', nls.localize('newFile', "New File"), tree, true, instantiationService.createInstance(CreateFileAction, element), null, fileService, messageService, textFileService); this.class = 'explorer-action new-file'; this._updateEnablement(); @@ -457,14 +437,12 @@ export class NewFolderAction extends BaseNewAction { constructor( tree: ITree, element: FileStat, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService, @IInstantiationService instantiationService: IInstantiationService ) { - super('workbench.action.files.newFolder', nls.localize('newFolder', "New Folder"), tree, false, instantiationService.createInstance(CreateFolderAction, element), null, contextService, editorService, fileService, messageService, textFileService); + super('workbench.action.files.newFolder', nls.localize('newFolder', "New Folder"), tree, false, instantiationService.createInstance(CreateFolderAction, element), null, fileService, messageService, textFileService); this.class = 'explorer-action new-folder'; this._updateEnablement(); @@ -581,13 +559,11 @@ export class CreateFileAction extends BaseCreateAction { constructor( element: FileStat, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService ) { - super(CreateFileAction.ID, CreateFileAction.LABEL, element, contextService, editorService, fileService, messageService, textFileService); + super(CreateFileAction.ID, CreateFileAction.LABEL, element, fileService, messageService, textFileService); this._updateEnablement(); } @@ -607,13 +583,11 @@ export class CreateFolderAction extends BaseCreateAction { constructor( element: FileStat, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService ) { - super(CreateFolderAction.ID, CreateFolderAction.LABEL, null, contextService, editorService, fileService, messageService, textFileService); + super(CreateFolderAction.ID, CreateFolderAction.LABEL, null, fileService, messageService, textFileService); this._updateEnablement(); } @@ -636,13 +610,11 @@ export class BaseDeleteFileAction extends BaseFileAction { tree: ITree, element: FileStat, useTrash: boolean, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService ) { - super(id, label, contextService, editorService, fileService, messageService, textFileService); + super(id, label, fileService, messageService, textFileService); this.tree = tree; this.element = element; @@ -759,13 +731,11 @@ export class MoveFileToTrashAction extends BaseDeleteFileAction { constructor( tree: ITree, element: FileStat, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService ) { - super(MoveFileToTrashAction.ID, nls.localize('delete', "Delete"), tree, element, true, contextService, editorService, fileService, messageService, textFileService); + super(MoveFileToTrashAction.ID, nls.localize('delete', "Delete"), tree, element, true, fileService, messageService, textFileService); } } @@ -779,13 +749,11 @@ export class ImportFileAction extends BaseFileAction { tree: ITree, element: FileStat, clazz: string, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService ) { - super(ImportFileAction.ID, nls.localize('importFiles', "Import Files"), contextService, editorService, fileService, messageService, textFileService); + super(ImportFileAction.ID, nls.localize('importFiles', "Import Files"), fileService, messageService, textFileService); this.tree = tree; this.element = element; @@ -887,13 +855,11 @@ export class CopyFileAction extends BaseFileAction { constructor( tree: ITree, element: FileStat, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService ) { - super(CopyFileAction.ID, nls.localize('copyFile', "Copy"), contextService, editorService, fileService, messageService, textFileService); + super(CopyFileAction.ID, nls.localize('copyFile', "Copy"), fileService, messageService, textFileService); this.tree = tree; this.element = element; @@ -926,14 +892,12 @@ export class PasteFileAction extends BaseFileAction { constructor( tree: ITree, element: FileStat, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService, @IInstantiationService private instantiationService: IInstantiationService ) { - super(PasteFileAction.ID, nls.localize('pasteFile', "Paste"), contextService, editorService, fileService, messageService, textFileService); + super(PasteFileAction.ID, nls.localize('pasteFile', "Paste"), fileService, messageService, textFileService); this.tree = tree; this.element = element; @@ -991,13 +955,11 @@ export class DuplicateFileAction extends BaseFileAction { tree: ITree, element: FileStat, target: FileStat, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService ) { - super('workbench.files.action.duplicateFile', nls.localize('duplicateFile', "Duplicate"), contextService, editorService, fileService, messageService, textFileService); + super('workbench.files.action.duplicateFile', nls.localize('duplicateFile', "Duplicate"), fileService, messageService, textFileService); this.tree = tree; this.element = element; diff --git a/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts b/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts index 2d49be0f862..36ae2efab2c 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts @@ -57,7 +57,7 @@ class FileViewerActionContributor extends ActionBarContributor { // Contribute Actions const category = nls.localize('filesCategory', "Files"); -const workbenchActionsRegistry = Registry.as(ActionExtensions.WorkbenchActions); +const workbenchActionsRegistry = Registry.as(ActionExtensions.WorkbenchActions); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SaveFileAsAction, SaveFileAsAction.ID, SaveFileAsAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_S }), 'Files: Save As...', category); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(GlobalNewUntitledFileAction, GlobalNewUntitledFileAction.ID, GlobalNewUntitledFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_N }), 'Files: New Untitled File', category); @@ -73,11 +73,11 @@ if (env.isMacintosh) { } // Contribute to File Viewers -const actionsRegistry = Registry.as(ActionBarExtensions.Actionbar); +const actionsRegistry = Registry.as(ActionBarExtensions.Actionbar); actionsRegistry.registerActionBarContributor(Scope.VIEWER, FileViewerActionContributor); // Register Dirty Files Tracker -(Registry.as(WorkbenchExtensions.Workbench)).registerWorkbenchContribution( +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( DirtyFilesTracker ); diff --git a/src/vs/workbench/parts/output/browser/output.contribution.ts b/src/vs/workbench/parts/output/browser/output.contribution.ts index c13bc270fc4..4a1e8491e36 100644 --- a/src/vs/workbench/parts/output/browser/output.contribution.ts +++ b/src/vs/workbench/parts/output/browser/output.contribution.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!../browser/media/output.contribution'; +import 'vs/css!./media/output.contribution'; import nls = require('vs/nls'); import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; -import platform = require('vs/platform/platform'); +import { Registry } from 'vs/platform/platform'; import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IKeybindings } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -16,7 +16,7 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/wor import { OutputService } from 'vs/workbench/parts/output/browser/outputServices'; import { ToggleOutputAction, ClearOutputAction } from 'vs/workbench/parts/output/browser/outputActions'; import { OUTPUT_MODE_ID, OUTPUT_MIME, OUTPUT_PANEL_ID, IOutputService } from 'vs/workbench/parts/output/common/output'; -import panel = require('vs/workbench/browser/panel'); +import { PanelRegistry, Extensions, PanelDescriptor } from 'vs/workbench/browser/panel'; import { EditorContextKeys } from 'vs/editor/common/editorCommon'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -33,7 +33,7 @@ ModesRegistry.registerLanguage({ }); // Register Output Panel -(platform.Registry.as(panel.Extensions.Panels)).registerPanel(new panel.PanelDescriptor( +Registry.as(Extensions.Panels).registerPanel(new PanelDescriptor( 'vs/workbench/parts/output/browser/outputPanel', 'OutputPanel', OUTPUT_PANEL_ID, @@ -43,7 +43,7 @@ ModesRegistry.registerLanguage({ )); // register toggle output action globally -let actionRegistry = platform.Registry.as(ActionExtensions.WorkbenchActions); +const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleOutputAction, ToggleOutputAction.ID, ToggleOutputAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_U, linux: { @@ -64,14 +64,14 @@ interface IActionDescriptor { iconClass?: string; f1?: boolean; - // + // menus menu?: { menuId: MenuId, when?: ContextKeyExpr; group?: string; }; - // + // keybindings keybinding?: { when?: ContextKeyExpr; weight: number; diff --git a/src/vs/workbench/parts/output/browser/outputActions.ts b/src/vs/workbench/parts/output/browser/outputActions.ts index f419657ff6f..94e258c45fe 100644 --- a/src/vs/workbench/parts/output/browser/outputActions.ts +++ b/src/vs/workbench/parts/output/browser/outputActions.ts @@ -90,6 +90,7 @@ export class SwitchOutputActionItem extends SelectActionItem { private static getChannelLabels(outputService: IOutputService): string[] { const contributedChannels = Registry.as(Extensions.OutputChannels).getChannels().map(channelData => channelData.label); + return contributedChannels.sort(); // sort by name } } diff --git a/src/vs/workbench/parts/output/browser/outputPanel.ts b/src/vs/workbench/parts/output/browser/outputPanel.ts index 362f0c87133..d1baef56afa 100644 --- a/src/vs/workbench/parts/output/browser/outputPanel.ts +++ b/src/vs/workbench/parts/output/browser/outputPanel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import nls = require('vs/nls'); -import lifecycle = require('vs/base/common/lifecycle'); +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; import { Action, IAction } from 'vs/base/common/actions'; import { Builder } from 'vs/base/browser/builder'; @@ -14,7 +14,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService } from 'vs/platform/storage/common/storage'; 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 { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; @@ -22,27 +21,21 @@ import { StringEditor } from 'vs/workbench/browser/parts/editor/stringEditor'; import { OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT } from 'vs/workbench/parts/output/common/output'; import { OutputEditorInput } from 'vs/workbench/parts/output/browser/outputEditorInput'; import { SwitchOutputAction, SwitchOutputActionItem, ClearOutputAction } from 'vs/workbench/parts/output/browser/outputActions'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IThemeService } from 'vs/workbench/services/themes/common/themeService'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; export class OutputPanel extends StringEditor { - - private toDispose: lifecycle.IDisposable[]; + private toDispose: IDisposable[]; private actions: IAction[]; private scopedInstantiationService: IInstantiationService; constructor( @ITelemetryService telemetryService: ITelemetryService, @IInstantiationService instantiationService: IInstantiationService, - @IWorkspaceContextService contextService: IWorkspaceContextService, @IStorageService storageService: IStorageService, - @IMessageService messageService: IMessageService, @IConfigurationService configurationService: IConfigurationService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IThemeService themeService: IThemeService, @IOutputService private outputService: IOutputService, @IUntitledEditorService untitledEditorService: IUntitledEditorService, @@ -50,8 +43,8 @@ export class OutputPanel extends StringEditor { @IEditorGroupService editorGroupService: IEditorGroupService, @ITextFileService textFileService: ITextFileService ) { - super(telemetryService, instantiationService, contextService, storageService, - messageService, configurationService, editorService, themeService, untitledEditorService, editorGroupService, textFileService); + super(telemetryService, instantiationService, storageService, configurationService, themeService, untitledEditorService, editorGroupService, textFileService); + this.scopedInstantiationService = instantiationService; this.toDispose = []; } @@ -105,6 +98,7 @@ export class OutputPanel extends StringEditor { } public createEditor(parent: Builder): void { + // First create the scoped instantation service and only then construct the editor using the scoped service const scopedContextKeyService = this.contextKeyService.createScoped(parent.getHTMLElement()); this.toDispose.push(scopedContextKeyService); @@ -120,7 +114,8 @@ export class OutputPanel extends StringEditor { } public dispose(): void { - this.toDispose = lifecycle.dispose(this.toDispose); + this.toDispose = dispose(this.toDispose); + super.dispose(); } } diff --git a/src/vs/workbench/parts/output/browser/outputServices.ts b/src/vs/workbench/parts/output/browser/outputServices.ts index 99bf2e7d57e..334de57560d 100644 --- a/src/vs/workbench/parts/output/browser/outputServices.ts +++ b/src/vs/workbench/parts/output/browser/outputServices.ts @@ -22,6 +22,7 @@ import { OutputLinkProvider } from 'vs/workbench/parts/output/common/outputLinkP const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel'; export class OutputService implements IOutputService { + public _serviceBrand: any; private receivedOutput: { [channel: string]: string; }; diff --git a/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts b/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts index 8ba65969ac6..a030a6fca95 100644 --- a/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts @@ -20,6 +20,8 @@ import { IPreferencesService, CONTEXT_DEFAULT_SETTINGS_EDITOR, DEFAULT_EDITOR_CO import { PreferencesService } from 'vs/workbench/parts/preferences/browser/preferencesService'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { PreferencesContentProvider } from 'vs/workbench/parts/preferences/common/preferencesContentProvider'; registerSingleton(IPreferencesService, PreferencesService); @@ -77,4 +79,6 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { }, when: ContextKeyExpr.and(CONTEXT_DEFAULT_SETTINGS_EDITOR), group: 'navigation' -}); \ No newline at end of file +}); + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(PreferencesContentProvider); \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 51d398365e8..197d0fb3dc5 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -43,11 +43,9 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/workbench/services/themes/common/themeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IMessageService } from 'vs/platform/message/common/message'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { ITextModelResolverService } from 'vs/editor/common/services/resolverService'; @@ -107,11 +105,8 @@ export class DefaultPreferencesEditor extends BaseTextEditor { constructor( @ITelemetryService telemetryService: ITelemetryService, @IInstantiationService instantiationService: IInstantiationService, - @IWorkspaceContextService contextService: IWorkspaceContextService, @IStorageService storageService: IStorageService, - @IMessageService messageService: IMessageService, @IConfigurationService configurationService: IConfigurationService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IThemeService themeService: IThemeService, @IUntitledEditorService private untitledEditorService: IUntitledEditorService, @IPreferencesService private preferencesService: IPreferencesService, @@ -119,7 +114,7 @@ export class DefaultPreferencesEditor extends BaseTextEditor { @IModeService private modeService: IModeService, @ITextFileService textFileService: ITextFileService ) { - super(DefaultPreferencesEditor.ID, telemetryService, instantiationService, contextService, storageService, messageService, configurationService, editorService, themeService, textFileService); + super(DefaultPreferencesEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService); this.delayedFilterLogging = new Delayer(1000); } diff --git a/src/vs/workbench/parts/contentprovider/common/contentprovider.contribution.ts b/src/vs/workbench/parts/preferences/common/preferencesContentProvider.ts similarity index 87% rename from src/vs/workbench/parts/contentprovider/common/contentprovider.contribution.ts rename to src/vs/workbench/parts/preferences/common/preferencesContentProvider.ts index a7e0a38b0e8..2d5ef5d8cb7 100644 --- a/src/vs/workbench/parts/contentprovider/common/contentprovider.contribution.ts +++ b/src/vs/workbench/parts/preferences/common/preferencesContentProvider.ts @@ -11,13 +11,13 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IModel } from 'vs/editor/common/editorCommon'; import JSONContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry'); import { Registry } from 'vs/platform/platform'; -import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ITextModelResolverService } from 'vs/editor/common/services/resolverService'; import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences'; const schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); -export class WorkbenchContentProvider implements IWorkbenchContribution { +export class PreferencesContentProvider implements IWorkbenchContribution { constructor( @IModelService private modelService: IModelService, @@ -58,6 +58,4 @@ export class WorkbenchContentProvider implements IWorkbenchContribution { } }); } -} - -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkbenchContentProvider); \ No newline at end of file +} \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts index ccfec2794a6..9aa26fe7ec1 100644 --- a/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts @@ -251,7 +251,7 @@ export class CommandsHandler extends QuickOpenHandler { // Workbench Actions (if prefix asks for all commands) let workbenchEntries: CommandEntry[] = []; if (this.includeWorkbenchCommands()) { - const workbenchActions = (Registry.as(ActionExtensions.WorkbenchActions)).getWorkbenchActions(); + const workbenchActions = Registry.as(ActionExtensions.WorkbenchActions).getWorkbenchActions(); workbenchEntries = this.actionDescriptorsToEntries(workbenchActions, searchValue); } diff --git a/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts b/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts index ed7e4dc3bcc..154b5eb08c0 100644 --- a/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts +++ b/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts @@ -16,6 +16,7 @@ import { GotoSymbolAction, GOTO_SYMBOL_PREFIX, SCOPE_PREFIX } from 'vs/workbench import { ShowAllCommandsAction, ALL_COMMANDS_PREFIX } from 'vs/workbench/parts/quickopen/browser/commandsHandler'; import { GotoLineAction, GOTO_LINE_PREFIX } from 'vs/workbench/parts/quickopen/browser/gotoLineHandler'; import { HELP_PREFIX } from 'vs/workbench/parts/quickopen/browser/helpHandler'; +import { VIEW_PICKER_PREFIX, OpenViewPickerAction, QuickOpenViewPickerAction } from 'vs/workbench/parts/quickopen/browser/viewPickerHandler'; // Register Actions let registry = Registry.as(ActionExtensions.WorkbenchActions); @@ -33,9 +34,14 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(GotoSymbolAction, Goto primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O }), 'Go to Symbol...'); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenViewPickerAction, OpenViewPickerAction.ID, OpenViewPickerAction.LABEL), 'Open View'); +registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenViewPickerAction, QuickOpenViewPickerAction.ID, QuickOpenViewPickerAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_Q, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_Q }, linux: { primary: null } +}), 'Quick Open View'); + // Register Quick Open Handler -(Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler( +Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( new QuickOpenHandlerDescriptor( 'vs/workbench/parts/quickopen/browser/commandsHandler', 'CommandsHandler', @@ -44,7 +50,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(GotoSymbolAction, Goto ) ); -(Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler( +Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( new QuickOpenHandlerDescriptor( 'vs/workbench/parts/quickopen/browser/gotoLineHandler', 'GotoLineHandler', @@ -59,7 +65,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(GotoSymbolAction, Goto ) ); -(Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler( +Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( new QuickOpenHandlerDescriptor( 'vs/workbench/parts/quickopen/browser/gotoSymbolHandler', 'GotoSymbolHandler', @@ -79,11 +85,26 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(GotoSymbolAction, Goto ) ); -(Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler( +Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( new QuickOpenHandlerDescriptor( 'vs/workbench/parts/quickopen/browser/helpHandler', 'HelpHandler', HELP_PREFIX, nls.localize('helpDescription', "Show Help") ) +); + +Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( + new QuickOpenHandlerDescriptor( + 'vs/workbench/parts/quickopen/browser/viewPickerHandler', + 'ViewPickerHandler', + VIEW_PICKER_PREFIX, + [ + { + prefix: VIEW_PICKER_PREFIX, + needsEditor: false, + description: nls.localize('viewPickerDescription', "Open View") + } + ] + ) ); \ No newline at end of file diff --git a/src/vs/workbench/parts/viewpicker/browser/viewPickerHandler.ts b/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts similarity index 100% rename from src/vs/workbench/parts/viewpicker/browser/viewPickerHandler.ts rename to src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts diff --git a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts index 2f54b603ac2..541a5d29ef8 100644 --- a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts +++ b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts @@ -19,9 +19,7 @@ import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; import { QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { QuickOpenHandler } from 'vs/workbench/browser/quickopen'; import { FileEntry, OpenFileHandler, FileQuickOpenModel } from 'vs/workbench/parts/search/browser/openFileHandler'; -/* tslint:disable:no-unused-variable */ import * as openSymbolHandler from 'vs/workbench/parts/search/browser/openSymbolHandler'; -/* tslint:enable:no-unused-variable */ import { IMessageService, Severity } from 'vs/platform/message/common/message'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ISearchStats, ICachedSearchStats, IUncachedSearchStats } from 'vs/platform/search/common/search'; diff --git a/src/vs/workbench/parts/viewpicker/browser/viewpicker.contribution.ts b/src/vs/workbench/parts/viewpicker/browser/viewpicker.contribution.ts deleted file mode 100644 index 9ab715cc1a1..00000000000 --- a/src/vs/workbench/parts/viewpicker/browser/viewpicker.contribution.ts +++ /dev/null @@ -1,33 +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 { Registry } from 'vs/platform/platform'; -import nls = require('vs/nls'); -import { QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions as QuickOpenExtensions } from 'vs/workbench/browser/quickopen'; -import { VIEW_PICKER_PREFIX, OpenViewPickerAction, QuickOpenViewPickerAction } from 'vs/workbench/parts/viewpicker/browser/viewPickerHandler'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; - -Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( - 'vs/workbench/parts/viewpicker/browser/viewPickerHandler', - 'ViewPickerHandler', - VIEW_PICKER_PREFIX, - [ - { - prefix: VIEW_PICKER_PREFIX, - needsEditor: false, - description: nls.localize('viewPickerDescription', "Open View") - } - ] - ) -); - -const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenViewPickerAction, OpenViewPickerAction.ID, OpenViewPickerAction.LABEL), 'Open View'); -registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenViewPickerAction, QuickOpenViewPickerAction.ID, QuickOpenViewPickerAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_Q, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_Q }, linux: { primary: null } }), 'Quick Open View'); diff --git a/src/vs/workbench/test/browser/part.test.ts b/src/vs/workbench/test/browser/part.test.ts index 89e92367803..06d23ce51f7 100644 --- a/src/vs/workbench/test/browser/part.test.ts +++ b/src/vs/workbench/test/browser/part.test.ts @@ -34,6 +34,10 @@ class MyPart extends Part { assert.strictEqual(parent, this.expectedParent); return super.createStatusArea(parent); } + + public getMemento(storageService: IStorageService): any { + return super.getMemento(storageService); + } } class MyPart2 extends Part { diff --git a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts index d084974040f..8607a049eb2 100644 --- a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts @@ -130,11 +130,11 @@ suite('Workbench BaseEditor', () => { let options = new EditorOptions(); assert(!e.isVisible()); - assert(!e.getInput()); - assert(!e.getOptions()); + assert(!e.input); + assert(!e.options); e.setInput(input, options).then(() => { - assert.strictEqual(input, e.getInput()); - assert.strictEqual(options, e.getOptions()); + assert.strictEqual(input, e.input); + assert.strictEqual(options, e.options); e.setVisible(true); assert(e.isVisible()); @@ -145,8 +145,8 @@ suite('Workbench BaseEditor', () => { e.clearInput(); e.setVisible(false); assert(!e.isVisible()); - assert(!e.getInput()); - assert(!e.getOptions()); + assert(!e.input); + assert(!e.options); assert(!e.getControl()); }).done(() => done()); }); From 19b321baf11308380771fd15449a792827a16707 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 28 Dec 2016 10:19:19 +0100 Subject: [PATCH 011/131] Adopt resource editor inputs for output (part of #17063) --- .../common/editor/stringEditorInput.ts | 20 --- .../common/editor/stringEditorModel.ts | 49 ------ .../parts/output/browser/outputEditorInput.ts | 145 ---------------- .../parts/output/browser/outputPanel.ts | 5 +- .../parts/output/browser/outputServices.ts | 162 +++++++++++++++++- .../workbench/parts/output/common/output.ts | 32 +++- .../common/editor/stringEditorInput.test.ts | 37 ++-- .../common/editor/stringEditorModel.test.ts | 28 +-- .../test/common/editor/untitledEditor.test.ts | 8 +- 9 files changed, 213 insertions(+), 273 deletions(-) delete mode 100644 src/vs/workbench/parts/output/browser/outputEditorInput.ts diff --git a/src/vs/workbench/common/editor/stringEditorInput.ts b/src/vs/workbench/common/editor/stringEditorInput.ts index 9e4d7656293..bf5b0489b87 100644 --- a/src/vs/workbench/common/editor/stringEditorInput.ts +++ b/src/vs/workbench/common/editor/stringEditorInput.ts @@ -74,26 +74,6 @@ export class StringEditorInput extends EditorInput { } } - /** - * Clears the textual value of this input and will also update the underlying model if this input is resolved. - */ - public clearValue(): void { - this.value = ''; - if (this.cachedModel) { - this.cachedModel.clearValue(); - } - } - - /** - * Appends to the textual value of this input and will also update the underlying model if this input is resolved. - */ - public append(value: string): void { - this.value += value; - if (this.cachedModel) { - this.cachedModel.append(value); - } - } - public resolve(refresh?: boolean): TPromise { // Use Cached Model diff --git a/src/vs/workbench/common/editor/stringEditorModel.ts b/src/vs/workbench/common/editor/stringEditorModel.ts index 82ba83b27a3..bee71a6bb7b 100644 --- a/src/vs/workbench/common/editor/stringEditorModel.ts +++ b/src/vs/workbench/common/editor/stringEditorModel.ts @@ -8,11 +8,8 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { EditorModel } from 'vs/workbench/common/editor'; import URI from 'vs/base/common/uri'; -import { Position } from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { EditOperation } from 'vs/editor/common/core/editOperation'; /** * An editor model whith an in-memory, readonly content that is not backed by any particular resource. @@ -53,52 +50,6 @@ export class StringEditorModel extends BaseTextEditorModel { } } - /** - * Appends value to this string editor model. - */ - public append(value: string): void { - this.value += value; - if (this.textEditorModel) { - let model = this.textEditorModel; - let lastLine = model.getLineCount(); - let lastLineMaxColumn = model.getLineMaxColumn(lastLine); - - model.applyEdits([EditOperation.insert(new Position(lastLine, lastLineMaxColumn), value)]); - } - } - - /** - * Clears the value of this string editor model - */ - public clearValue(): void { - this.value = ''; - if (this.textEditorModel) { - let model = this.textEditorModel; - let lastLine = model.getLineCount(); - model.applyEdits([EditOperation.delete(new Range(1, 1, lastLine, model.getLineMaxColumn(lastLine)))]); - } - } - - /** - * Removes all lines from the top if the line number exceeds the given line count. Returns the new value if lines got trimmed. - */ - public trim(linecount: number): string { - if (this.textEditorModel) { - let model = this.textEditorModel; - let lastLine = model.getLineCount(); - if (lastLine > linecount) { - model.applyEdits([EditOperation.delete(new Range(1, 1, lastLine - linecount + 1, 1))]); - - let newValue = model.getValue(); - this.value = newValue; - - return this.value; - } - } - - return null; - } - public load(): TPromise { // Create text editor model if not yet done diff --git a/src/vs/workbench/parts/output/browser/outputEditorInput.ts b/src/vs/workbench/parts/output/browser/outputEditorInput.ts deleted file mode 100644 index 1b9e457be68..00000000000 --- a/src/vs/workbench/parts/output/browser/outputEditorInput.ts +++ /dev/null @@ -1,145 +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 nls = require('vs/nls'); -import lifecycle = require('vs/base/common/lifecycle'); -import strings = require('vs/base/common/strings'); -import { TPromise } from 'vs/base/common/winjs.base'; -import { RunOnceScheduler } from 'vs/base/common/async'; -import { EditorModel } from 'vs/workbench/common/editor'; -import { StringEditorInput } from 'vs/workbench/common/editor/stringEditorInput'; -import { OUTPUT_EDITOR_INPUT_ID, OUTPUT_PANEL_ID, IOutputEvent, OUTPUT_MIME, IOutputService, MAX_OUTPUT_LENGTH, IOutputChannel } from 'vs/workbench/parts/output/common/output'; -import { OutputPanel } from 'vs/workbench/parts/output/browser/outputPanel'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; - -/** - * Output Editor Input - */ -export class OutputEditorInput extends StringEditorInput { - - private static OUTPUT_DELAY = 300; // delay in ms to accumulate output before emitting an event about it - private static instances: { [channel: string]: OutputEditorInput; } = Object.create(null); - - private outputSet: boolean; - private bufferedOutput: string; - private toDispose: lifecycle.IDisposable[]; - private appendOutputScheduler: RunOnceScheduler; - - public static getInstances(): OutputEditorInput[] { - return Object.keys(OutputEditorInput.instances).map((key) => OutputEditorInput.instances[key]); - } - - public static getInstance(instantiationService: IInstantiationService, channel: IOutputChannel): OutputEditorInput { - if (OutputEditorInput.instances[channel.id]) { - return OutputEditorInput.instances[channel.id]; - } - - OutputEditorInput.instances[channel.id] = instantiationService.createInstance(OutputEditorInput, channel); - - return OutputEditorInput.instances[channel.id]; - } - - constructor( - private outputChannel: IOutputChannel, - @IInstantiationService instantiationService: IInstantiationService, - @IOutputService private outputService: IOutputService, - @IPanelService private panelService: IPanelService - ) { - super(nls.localize('output', "Output"), outputChannel ? nls.localize('outputChannel', "for '{0}'", outputChannel.label) : '', '', OUTPUT_MIME, true, instantiationService); - - this.bufferedOutput = ''; - this.toDispose = []; - this.toDispose.push(this.outputService.onOutput(this.onOutputReceived, this)); - this.toDispose.push(this.outputService.onActiveOutputChannel(() => this.scheduleOutputAppend())); - this.toDispose.push(this.panelService.onDidPanelOpen(panel => { - if (panel.getId() === OUTPUT_PANEL_ID) { - this.appendOutput(); - } - })); - - this.appendOutputScheduler = new RunOnceScheduler(() => { - if (this.isVisible()) { - this.appendOutput(); - } - }, OutputEditorInput.OUTPUT_DELAY); - } - - private appendOutput(): void { - if (this.bufferedOutput.length === 0) { - return; - } - - if (this.value.length + this.bufferedOutput.length > MAX_OUTPUT_LENGTH) { - this.setValue(this.outputChannel.output); - } else { - this.append(this.bufferedOutput); - } - this.bufferedOutput = ''; - - const panel = this.panelService.getActivePanel(); - (panel).revealLastLine(true); - } - - private onOutputReceived(e: IOutputEvent): void { - if (this.outputSet && e.channelId === this.outputChannel.id) { - if (e.output) { - this.bufferedOutput = strings.appendWithLimit(this.bufferedOutput, e.output, MAX_OUTPUT_LENGTH); - this.scheduleOutputAppend(); - } else if (e.output === null) { - this.bufferedOutput = ''; - this.clearValue(); // special output indicates we should clear - } - } - } - - private isVisible(): boolean { - const panel = this.panelService.getActivePanel(); - return panel && panel.getId() === OUTPUT_PANEL_ID && this.outputService.getActiveChannel().id === this.outputChannel.id; - } - - private scheduleOutputAppend(): void { - if (this.isVisible() && this.bufferedOutput && !this.appendOutputScheduler.isScheduled()) { - this.appendOutputScheduler.schedule(); - } - } - - public getTypeId(): string { - return OUTPUT_EDITOR_INPUT_ID; - } - - public resolve(refresh?: boolean): TPromise { - return super.resolve(refresh).then(model => { - // Just return model if output already set - if (this.outputSet) { - return model; - } - - this.setValue(this.outputChannel.output); - this.outputSet = true; - - return model; - }); - } - - public matches(otherInput: any): boolean { - if (otherInput instanceof OutputEditorInput) { - let otherOutputEditorInput = otherInput; - if (otherOutputEditorInput.outputChannel.id === this.outputChannel.id) { - return super.matches(otherInput); - } - } - - return false; - } - - public dispose(): void { - this.appendOutputScheduler.dispose(); - this.toDispose = lifecycle.dispose(this.toDispose); - - super.dispose(); - } -} diff --git a/src/vs/workbench/parts/output/browser/outputPanel.ts b/src/vs/workbench/parts/output/browser/outputPanel.ts index d1baef56afa..fea1339e8c0 100644 --- a/src/vs/workbench/parts/output/browser/outputPanel.ts +++ b/src/vs/workbench/parts/output/browser/outputPanel.ts @@ -18,8 +18,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { StringEditor } from 'vs/workbench/browser/parts/editor/stringEditor'; -import { OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT } from 'vs/workbench/parts/output/common/output'; -import { OutputEditorInput } from 'vs/workbench/parts/output/browser/outputEditorInput'; +import { OutputEditors, OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT } from 'vs/workbench/parts/output/common/output'; import { SwitchOutputAction, SwitchOutputActionItem, ClearOutputAction } from 'vs/workbench/parts/output/browser/outputActions'; import { IThemeService } from 'vs/workbench/services/themes/common/themeService'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; @@ -106,7 +105,7 @@ export class OutputPanel extends StringEditor { super.createEditor(parent); CONTEXT_IN_OUTPUT.bindTo(scopedContextKeyService).set(true); - this.setInput(OutputEditorInput.getInstance(this.instantiationService, this.outputService.getActiveChannel()), null); + this.setInput(OutputEditors.getInstance(this.instantiationService, this.outputService.getActiveChannel()), null); } public get instantiationService(): IInstantiationService { diff --git a/src/vs/workbench/parts/output/browser/outputServices.ts b/src/vs/workbench/parts/output/browser/outputServices.ts index 334de57560d..cd5d58d457a 100644 --- a/src/vs/workbench/parts/output/browser/outputServices.ts +++ b/src/vs/workbench/parts/output/browser/outputServices.ts @@ -6,18 +6,25 @@ import { TPromise } from 'vs/base/common/winjs.base'; import strings = require('vs/base/common/strings'); import Event, { Emitter } from 'vs/base/common/event'; +import URI from 'vs/base/common/uri'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IEditor } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/platform'; import { EditorOptions } from 'vs/workbench/common/editor'; -import { IOutputEvent, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, MAX_OUTPUT_LENGTH } from 'vs/workbench/parts/output/common/output'; -import { OutputEditorInput } from 'vs/workbench/parts/output/browser/outputEditorInput'; +import { OutputEditors, IOutputEvent, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, MAX_OUTPUT_LENGTH, OUTPUT_SCHEME, OUTPUT_MIME } from 'vs/workbench/parts/output/common/output'; import { OutputPanel } from 'vs/workbench/parts/output/browser/outputPanel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { OutputLinkProvider } from 'vs/workbench/parts/output/common/outputLinkProvider'; +import { ITextModelResolverService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; +import { IModel } from 'vs/editor/common/editorCommon'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position } from 'vs/editor/common/core/position'; const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel'; @@ -40,7 +47,8 @@ export class OutputService implements IOutputService { @IInstantiationService private instantiationService: IInstantiationService, @IPanelService private panelService: IPanelService, @IWorkspaceContextService contextService: IWorkspaceContextService, - @IModelService modelService: IModelService + @IModelService modelService: IModelService, + @ITextModelResolverService textModelResolverService: ITextModelResolverService ) { this._onOutput = new Emitter(); this._onOutputChannel = new Emitter(); @@ -52,6 +60,9 @@ export class OutputService implements IOutputService { this.activeChannelId = this.storageService.get(OUTPUT_ACTIVE_CHANNEL_KEY, StorageScope.WORKSPACE, channels && channels.length > 0 ? channels[0].id : null); this._outputLinkDetector = new OutputLinkProvider(contextService, modelService); + + // Register as text model content provider for output + textModelResolverService.registerTextModelContentProvider(OUTPUT_SCHEME, instantiationService.createInstance(OutputContentProvider, this)); } public get onOutput(): Event { @@ -127,8 +138,151 @@ export class OutputService implements IOutputService { this._onActiveOutputChannel.fire(channelId); // emit event that a new channel is active return this.panelService.openPanel(OUTPUT_PANEL_ID, !preserveFocus).then((outputPanel: OutputPanel) => { - return outputPanel && outputPanel.setInput(OutputEditorInput.getInstance(this.instantiationService, this.getChannel(channelId)), EditorOptions.create({ preserveFocus: preserveFocus })). + return outputPanel && outputPanel.setInput(OutputEditors.getInstance(this.instantiationService, this.getChannel(channelId)), EditorOptions.create({ preserveFocus: preserveFocus })). then(() => outputPanel); }); } +} + +class OutputContentProvider implements ITextModelContentProvider { + + private static OUTPUT_DELAY = 300; + + private bufferedOutput: { [channel: string]: string; }; + private appendOutputScheduler: { [channel: string]: RunOnceScheduler; }; + + private toDispose: IDisposable[]; + + constructor( + private outputService: IOutputService, + @IModelService private modelService: IModelService, + @IModeService private modeService: IModeService, + @IPanelService private panelService: IPanelService + ) { + this.bufferedOutput = Object.create(null); + this.appendOutputScheduler = Object.create(null); + this.toDispose = []; + + this.registerListeners(); + } + + private registerListeners(): void { + this.toDispose.push(this.outputService.onOutput(e => this.onOutputReceived(e))); + this.toDispose.push(this.outputService.onActiveOutputChannel(channel => this.scheduleOutputAppend(channel))); + this.toDispose.push(this.panelService.onDidPanelOpen(panel => { + if (panel.getId() === OUTPUT_PANEL_ID) { + this.appendOutput(); + } + })); + } + + private onOutputReceived(e: IOutputEvent): void { + const model = this.getModel(e.channelId); + if (!model) { + return; // only react if we have a known model + } + + // Append to model + if (e.output) { + this.bufferedOutput[e.channelId] = strings.appendWithLimit(this.bufferedOutput[e.channelId] || '', e.output, MAX_OUTPUT_LENGTH); + this.scheduleOutputAppend(e.channelId); + } + + // Clear from model + else if (e.output === null) { + this.bufferedOutput[e.channelId] = ''; + model.setValue(''); + } + } + + private getModel(channel: string): IModel { + return this.modelService.getModel(URI.from({ scheme: OUTPUT_SCHEME, path: channel })); + } + + private scheduleOutputAppend(channel: string): void { + if (!this.isVisible(channel)) { + return; // only if the output channel is visible + } + + if (!this.bufferedOutput[channel]) { + return; // only if we have any output to show + } + + let scheduler = this.appendOutputScheduler[channel]; + if (!scheduler) { + scheduler = new RunOnceScheduler(() => { + if (this.isVisible(channel)) { + this.appendOutput(channel); + } + }, OutputContentProvider.OUTPUT_DELAY); + + this.appendOutputScheduler[channel] = scheduler; + this.toDispose.push(scheduler); + } + + if (scheduler.isScheduled()) { + return; // only if not already scheduled + } + + scheduler.schedule(); + } + + private appendOutput(channel?: string): void { + if (!channel) { + const activeChannel = this.outputService.getActiveChannel(); + channel = activeChannel && activeChannel.id; + } + + if (!channel) { + return; // return if we do not have a valid channel to append to + } + + const model = this.getModel(channel); + if (!model) { + return; // only react if we have a known model + } + + const bufferedOutput = this.bufferedOutput[channel]; + if (!bufferedOutput) { + return; // return if nothing to append + } + + // just fill in the full (trimmed) output if we exceed max length + if (model.getValueLength() + bufferedOutput.length > MAX_OUTPUT_LENGTH) { + model.setValue(this.outputService.getChannel(channel).output); + } + + // otherwise append + else { + const lastLine = model.getLineCount(); + const lastLineMaxColumn = model.getLineMaxColumn(lastLine); + + model.applyEdits([EditOperation.insert(new Position(lastLine, lastLineMaxColumn), bufferedOutput)]); + } + + // reveal last line + const panel = this.panelService.getActivePanel(); + (panel).revealLastLine(true); + } + + private isVisible(channel: string): boolean { + const panel = this.panelService.getActivePanel(); + + return panel && panel.getId() === OUTPUT_PANEL_ID && this.outputService.getActiveChannel().id === channel; + } + + public provideTextContent(resource: URI): TPromise { + const content = this.outputService.getChannel(resource.fsPath).output; + + let codeEditorModel = this.modelService.getModel(resource); + if (!codeEditorModel) { + codeEditorModel = this.modelService.createModel(content, this.modeService.getOrCreateMode(OUTPUT_MIME), resource); + } + + return TPromise.as(codeEditorModel); + } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + } } \ No newline at end of file diff --git a/src/vs/workbench/parts/output/common/output.ts b/src/vs/workbench/parts/output/common/output.ts index 9e2e8d41c10..eb3de6fec37 100644 --- a/src/vs/workbench/parts/output/common/output.ts +++ b/src/vs/workbench/parts/output/common/output.ts @@ -7,25 +7,28 @@ import { TPromise } from 'vs/base/common/winjs.base'; import Event from 'vs/base/common/event'; import { Registry } from 'vs/platform/platform'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditor } from 'vs/platform/editor/common/editor'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import nls = require('vs/nls'); +import URI from 'vs/base/common/uri'; /** * Mime type used by the output editor. */ export const OUTPUT_MIME = 'text/x-code-output'; +/** + * Output resource scheme. + */ +export const OUTPUT_SCHEME = 'output'; + /** * Id used by the output editor. */ export const OUTPUT_MODE_ID = 'Log'; -/** - * Output editor input id. - */ -export const OUTPUT_EDITOR_INPUT_ID = 'vs.output'; - /** * Output panel id */ @@ -150,3 +153,20 @@ class OutputChannelRegistry implements IOutputChannelRegistry { } Registry.add(Extensions.OutputChannels, new OutputChannelRegistry()); + +export class OutputEditors { + + private static instances: { [channel: string]: ResourceEditorInput; } = Object.create(null); + + public static getInstance(instantiationService: IInstantiationService, channel: IOutputChannel): ResourceEditorInput { + if (OutputEditors.instances[channel.id]) { + return OutputEditors.instances[channel.id]; + } + + const resource = URI.from({ scheme: OUTPUT_SCHEME, path: channel.id }); + + OutputEditors.instances[channel.id] = instantiationService.createInstance(ResourceEditorInput, nls.localize('output', "Output"), channel ? nls.localize('channel', "for '{0}'", channel.label) : '', resource); + + return OutputEditors.instances[channel.id]; + } +} \ No newline at end of file diff --git a/src/vs/workbench/test/common/editor/stringEditorInput.test.ts b/src/vs/workbench/test/common/editor/stringEditorInput.test.ts index b6b2926d710..9d4fda893a1 100644 --- a/src/vs/workbench/test/common/editor/stringEditorInput.test.ts +++ b/src/vs/workbench/test/common/editor/stringEditorInput.test.ts @@ -20,7 +20,6 @@ import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import WorkbenchEditorService = require('vs/workbench/services/editor/common/editorService'); suite('Workbench - StringEditorInput', () => { - let instantiationService: TestInstantiationService; let editorService: WorkbenchEditorService.IWorkbenchEditorService; let modelService: IModelService; @@ -34,13 +33,12 @@ suite('Workbench - StringEditorInput', () => { }); test('StringEditorInput', function (done) { + let input: StringEditorInput = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'value', 'mode', false); + const otherInput: StringEditorInput = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'othervalue', 'mode', false); + const otherInputSame: StringEditorInput = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'value', 'mode', false); - let input = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'value', 'mode', false); - let otherInput = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'othervalue', 'mode', false); - let otherInputSame = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'value', 'mode', false); - - let inputSingleton = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'value', 'mode', true); - let otherInputSingleton = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'othervalue', 'mode', true); + const inputSingleton: StringEditorInput = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'value', 'mode', true); + const otherInputSingleton: StringEditorInput = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'othervalue', 'mode', true); assert(inputSingleton.matches(otherInputSingleton)); (otherInputSingleton).singleton = false; assert(!inputSingleton.matches(otherInputSingleton)); @@ -55,11 +53,11 @@ suite('Workbench - StringEditorInput', () => { input = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'value', 'mode', false); input.resolve(true).then(resolved => { - let resolvedModelA = resolved; + const resolvedModelA = resolved; return input.resolve(true).then(resolved => { assert(resolvedModelA === resolved); // assert: Resolved Model cached per instance - let otherInput = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'value', 'mode', false); + const otherInput: StringEditorInput = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'value', 'mode', false); return otherInput.resolve(true).then(resolved => { assert(resolvedModelA !== resolved); // NOT assert: Different instance, different model @@ -68,7 +66,7 @@ suite('Workbench - StringEditorInput', () => { return input.resolve(true).then(resolved => { assert(resolvedModelA !== resolved); // Different instance, because input got disposed - let model = (resolved).textEditorModel; + const model = (resolved).textEditorModel; return input.resolve(true).then(againResolved => { assert(model === (againResolved).textEditorModel); // Models should not differ because string input is constant @@ -81,27 +79,23 @@ suite('Workbench - StringEditorInput', () => { }); test('StringEditorInput - setValue, clearValue, append', function () { - let input = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'value', 'mode', false); + const input: StringEditorInput = instantiationService.createInstance(StringEditorInput, 'name', 'description', 'value', 'mode', false); assert.strictEqual(input.getValue(), 'value'); input.setValue('foo'); assert.strictEqual(input.getValue(), 'foo'); - input.clearValue(); + input.setValue(''); assert(!input.getValue()); - input.append('1'); - assert.strictEqual(input.getValue(), '1'); - input.append('2'); - assert.strictEqual(input.getValue(), '12'); }); test('Input.matches() - StringEditorInput', function () { - let inst = new TestInstantiationService(); + const inst = new TestInstantiationService(); - let stringEditorInput = inst.createInstance(StringEditorInput, 'name', 'description', 'value', 'mode', false); - let promiseEditorInput = inst.createInstance(ResourceEditorInput, 'name', 'description', URI.from({ scheme: 'inMemory', authority: null, path: 'thePath' })); + const stringEditorInput: StringEditorInput = inst.createInstance(StringEditorInput, 'name', 'description', 'value', 'mode', false); + const promiseEditorInput: StringEditorInput = inst.createInstance(ResourceEditorInput, 'name', 'description', URI.from({ scheme: 'inMemory', authority: null, path: 'thePath' })); - let stringEditorInput2 = inst.createInstance(StringEditorInput, 'name', 'description', 'value', 'mode', false); - let promiseEditorInput2 = inst.createInstance(ResourceEditorInput, 'name', 'description', URI.from({ scheme: 'inMemory', authority: null, path: 'thePath' })); + const stringEditorInput2: StringEditorInput = inst.createInstance(StringEditorInput, 'name', 'description', 'value', 'mode', false); + const promiseEditorInput2: StringEditorInput = inst.createInstance(ResourceEditorInput, 'name', 'description', URI.from({ scheme: 'inMemory', authority: null, path: 'thePath' })); assert.strictEqual(stringEditorInput.matches(null), false); assert.strictEqual(promiseEditorInput.matches(null), false); @@ -115,6 +109,7 @@ suite('Workbench - StringEditorInput', () => { function stubModelService(instantiationService: TestInstantiationService): IModelService { instantiationService.stub(IConfigurationService, new TestConfigurationService()); + return instantiationService.createInstance(ModelServiceImpl); } }); \ No newline at end of file diff --git a/src/vs/workbench/test/common/editor/stringEditorModel.test.ts b/src/vs/workbench/test/common/editor/stringEditorModel.test.ts index fb28147b5b8..8258b2c28b2 100644 --- a/src/vs/workbench/test/common/editor/stringEditorModel.test.ts +++ b/src/vs/workbench/test/common/editor/stringEditorModel.test.ts @@ -16,7 +16,6 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; suite('Workbench - StringEditorModel', () => { - let instantiationService: TestInstantiationService; setup(() => { @@ -26,11 +25,11 @@ suite('Workbench - StringEditorModel', () => { test('StringEditorModel', function (done) { instantiationService.stub(IModelService, stubModelService(instantiationService)); - let m = instantiationService.createInstance(StringEditorModel, 'value', 'mode', null); + const m: StringEditorModel = instantiationService.createInstance(StringEditorModel, 'value', 'mode', null); m.load().then(model => { assert(model === m); - let textEditorModel = m.textEditorModel; + const textEditorModel = m.textEditorModel; assert.strictEqual(textEditorModel.getValue(), 'value'); assert.strictEqual(m.isResolved(), true); @@ -46,36 +45,22 @@ suite('Workbench - StringEditorModel', () => { }); }); - test('StringEditorModel - setValue, clearValue, append, trim', function (done) { + test('StringEditorModel - setValue', function (done) { instantiationService.stub(IModelService, stubModelService(instantiationService)); - let m = instantiationService.createInstance(StringEditorModel, 'value', 'mode', null); + const m: StringEditorModel = instantiationService.createInstance(StringEditorModel, 'value', 'mode', null); m.load().then(model => { assert(model === m); - let textEditorModel = m.textEditorModel; + const textEditorModel = m.textEditorModel; assert.strictEqual(textEditorModel.getValue(), 'value'); m.setValue('foobar'); assert.strictEqual(m.getValue(), 'foobar'); assert.strictEqual(textEditorModel.getValue(), 'foobar'); - m.clearValue(); + m.setValue(''); assert(!m.getValue()); assert(!textEditorModel.getValue()); - - m.append('1'); - assert.strictEqual(m.getValue(), '1'); - assert.strictEqual(textEditorModel.getValue(), '1'); - - m.append('1'); - assert.strictEqual(m.getValue(), '11'); - assert.strictEqual(textEditorModel.getValue(), '11'); - - m.setValue('line\nline\nline'); - m.trim(2); - - assert.strictEqual(m.getValue(), 'line\nline'); - assert.strictEqual(textEditorModel.getValue(), 'line\nline'); }).done(() => { m.dispose(); done(); @@ -84,6 +69,7 @@ suite('Workbench - StringEditorModel', () => { function stubModelService(instantiationService: TestInstantiationService): IModelService { instantiationService.stub(IConfigurationService, new TestConfigurationService()); + return instantiationService.createInstance(ModelServiceImpl); } }); \ No newline at end of file diff --git a/src/vs/workbench/test/common/editor/untitledEditor.test.ts b/src/vs/workbench/test/common/editor/untitledEditor.test.ts index a4ca3e9ce16..0da7ca4eada 100644 --- a/src/vs/workbench/test/common/editor/untitledEditor.test.ts +++ b/src/vs/workbench/test/common/editor/untitledEditor.test.ts @@ -162,21 +162,21 @@ suite('Workbench - Untitled Editor', () => { }); input.resolve().then((model: UntitledEditorModel) => { - model.append('foo'); + model.setValue('foo'); assert.equal(counter, 0, 'Dirty model should not trigger event immediately'); TPromise.timeout(3).then(() => { assert.equal(counter, 1, 'Dirty model should trigger event'); - model.append('bar'); + model.setValue('bar'); TPromise.timeout(3).then(() => { assert.equal(counter, 2, 'Content change when dirty should trigger event'); - model.clearValue(); + model.setValue(''); TPromise.timeout(3).then(() => { assert.equal(counter, 3, 'Manual revert should trigger event'); - model.append('foo'); + model.setValue('foo'); TPromise.timeout(3).then(() => { assert.equal(counter, 4, 'Dirty model should trigger event'); From bb71a2eafccbb3ab9dd02f3843d6354e386dbb7f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 28 Dec 2016 10:34:04 +0100 Subject: [PATCH 012/131] debt - avoid stringeditormodel for untitled --- .../common/editor/untitledEditorModel.ts | 33 +++++++++++++------ .../textfile/test/textFileService.test.ts | 2 +- .../test/common/editor/untitledEditor.test.ts | 8 ++--- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index 6d4507fa1f4..9f9679e786c 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -7,7 +7,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; import { EditorModel, IEncodingSupport } from 'vs/workbench/common/editor'; -import { StringEditorModel } from 'vs/workbench/common/editor/stringEditorModel'; +import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import URI from 'vs/base/common/uri'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { EndOfLinePreference } from 'vs/editor/common/editorCommon'; @@ -21,7 +21,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { IBackupFileService, BACKUP_FILE_RESOLVE_OPTIONS } from 'vs/workbench/services/backup/common/backup'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -export class UntitledEditorModel extends StringEditorModel implements IEncodingSupport { +export class UntitledEditorModel extends BaseTextEditorModel implements IEncodingSupport { public static DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = CONTENT_CHANGE_EVENT_BUFFER_DELAY; @@ -43,8 +43,8 @@ export class UntitledEditorModel extends StringEditorModel implements IEncodingS private hasAssociatedFilePath: boolean; constructor( - modeId: string, - resource: URI, + private modeId: string, + private resource: URI, hasAssociatedFilePath: boolean, @IModeService modeService: IModeService, @IModelService modelService: IModelService, @@ -52,7 +52,7 @@ export class UntitledEditorModel extends StringEditorModel implements IEncodingS @ITextFileService private textFileService: ITextFileService, @IConfigurationService private configurationService: IConfigurationService ) { - super('', modeId, resource, modeService, modelService); + super(modelService, modeService); this.hasAssociatedFilePath = hasAssociatedFilePath; this.dirty = false; @@ -167,13 +167,11 @@ export class UntitledEditorModel extends StringEditorModel implements IEncodingS return null; }).then(backupContent => { - if (backupContent) { - this.setValue(backupContent); - } - this.setDirty(this.hasAssociatedFilePath || !!backupContent); // untitled associated to file path are dirty right away as well as untitled with content + // untitled associated to file path are dirty right away as well as untitled with content + this.setDirty(this.hasAssociatedFilePath || !!backupContent); - return super.load().then(model => { + return this.doLoad(backupContent || '').then(model => { const configuration = this.configurationService.getConfiguration(); // Encoding @@ -187,6 +185,21 @@ export class UntitledEditorModel extends StringEditorModel implements IEncodingS }); } + private doLoad(content: string): TPromise { + + // Create text editor model if not yet done + if (!this.textEditorModel) { + return this.createTextEditorModel(content, this.resource, this.modeId); + } + + // Otherwise update + else { + this.updateTextEditorModel(content); + } + + return TPromise.as(this); + } + private onModelContentChanged(): void { this.versionId++; diff --git a/src/vs/workbench/services/textfile/test/textFileService.test.ts b/src/vs/workbench/services/textfile/test/textFileService.test.ts index b10e5b3ecd1..4d3887107d8 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.test.ts @@ -163,7 +163,7 @@ suite('Files - TextFileService', () => { return untitled.resolve().then((model: UntitledEditorModel) => { assert.ok(!service.isDirty(untitled.getResource())); assert.equal(service.getDirty().length, 1); - model.setValue('changed'); + model.textEditorModel.setValue('changed'); assert.ok(service.isDirty(untitled.getResource())); assert.equal(service.getDirty().length, 2); diff --git a/src/vs/workbench/test/common/editor/untitledEditor.test.ts b/src/vs/workbench/test/common/editor/untitledEditor.test.ts index 0da7ca4eada..251def9a044 100644 --- a/src/vs/workbench/test/common/editor/untitledEditor.test.ts +++ b/src/vs/workbench/test/common/editor/untitledEditor.test.ts @@ -162,21 +162,21 @@ suite('Workbench - Untitled Editor', () => { }); input.resolve().then((model: UntitledEditorModel) => { - model.setValue('foo'); + model.textEditorModel.setValue('foo'); assert.equal(counter, 0, 'Dirty model should not trigger event immediately'); TPromise.timeout(3).then(() => { assert.equal(counter, 1, 'Dirty model should trigger event'); - model.setValue('bar'); + model.textEditorModel.setValue('bar'); TPromise.timeout(3).then(() => { assert.equal(counter, 2, 'Content change when dirty should trigger event'); - model.setValue(''); + model.textEditorModel.setValue(''); TPromise.timeout(3).then(() => { assert.equal(counter, 3, 'Manual revert should trigger event'); - model.setValue('foo'); + model.textEditorModel.setValue('foo'); TPromise.timeout(3).then(() => { assert.equal(counter, 4, 'Dirty model should trigger event'); From 3aab0f33e68ffb03e8b0ba947e2dc6a2bade4dca Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 28 Dec 2016 10:45:39 +0100 Subject: [PATCH 013/131] debt - string editor => text resource editor --- .../browser/parts/editor/editor.contribution.ts | 8 ++++---- .../editor/{stringEditor.ts => textResourceEditor.ts} | 10 +++++----- src/vs/workbench/parts/output/browser/outputPanel.ts | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) rename src/vs/workbench/browser/parts/editor/{stringEditor.ts => textResourceEditor.ts} (94%) diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 842d7a4f1d4..bccd08207a4 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -14,7 +14,7 @@ import { StatusbarItemDescriptor, StatusbarAlignment, IStatusbarRegistry, Extens import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorInput, IEditorRegistry, Extensions as EditorExtensions, IEditorInputFactory, SideBySideEditorInput } from 'vs/workbench/common/editor'; import { StringEditorInput } from 'vs/workbench/common/editor/stringEditorInput'; -import { StringEditor } from 'vs/workbench/browser/parts/editor/stringEditor'; +import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; @@ -44,10 +44,10 @@ import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommand // Register String Editor Registry.as(EditorExtensions.Editors).registerEditor( new EditorDescriptor( - StringEditor.ID, + TextResourceEditor.ID, nls.localize('textEditor', "Text Editor"), - 'vs/workbench/browser/parts/editor/stringEditor', - 'StringEditor' + 'vs/workbench/browser/parts/editor/textResourceEditor', + 'TextResourceEditor' ), [ new SyncDescriptor(StringEditorInput), diff --git a/src/vs/workbench/browser/parts/editor/stringEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts similarity index 94% rename from src/vs/workbench/browser/parts/editor/stringEditor.ts rename to src/vs/workbench/browser/parts/editor/textResourceEditor.ts index be788f01aff..88871b501c4 100644 --- a/src/vs/workbench/browser/parts/editor/stringEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -24,12 +24,12 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; /** - * An editor implementation that is capable of showing string inputs or promise inputs that resolve to a string. - * Uses the TextEditor widget to show the contents. + * An editor implementation that is capable of showing the contents of resource inputs. Uses + * the TextEditor widget to show the contents. */ -export class StringEditor extends BaseTextEditor { +export class TextResourceEditor extends BaseTextEditor { - public static ID = 'workbench.editors.stringEditor'; + public static ID = 'workbench.editors.textResourceEditor'; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -41,7 +41,7 @@ export class StringEditor extends BaseTextEditor { @IEditorGroupService private editorGroupService: IEditorGroupService, @ITextFileService textFileService: ITextFileService ) { - super(StringEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService); + super(TextResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService); this.toUnbind.push(this.untitledEditorService.onDidChangeDirty(e => this.onUntitledDirtyChange(e))); } diff --git a/src/vs/workbench/parts/output/browser/outputPanel.ts b/src/vs/workbench/parts/output/browser/outputPanel.ts index fea1339e8c0..3967a3dae21 100644 --- a/src/vs/workbench/parts/output/browser/outputPanel.ts +++ b/src/vs/workbench/parts/output/browser/outputPanel.ts @@ -17,7 +17,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; -import { StringEditor } from 'vs/workbench/browser/parts/editor/stringEditor'; +import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; import { OutputEditors, OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT } from 'vs/workbench/parts/output/common/output'; import { SwitchOutputAction, SwitchOutputActionItem, ClearOutputAction } from 'vs/workbench/parts/output/browser/outputActions'; import { IThemeService } from 'vs/workbench/services/themes/common/themeService'; @@ -25,7 +25,7 @@ import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/un import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; -export class OutputPanel extends StringEditor { +export class OutputPanel extends TextResourceEditor { private toDispose: IDisposable[]; private actions: IAction[]; private scopedInstantiationService: IInstantiationService; From 716981d311509a768133c61cd22ce29045b42f61 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 28 Dec 2016 11:42:00 +0100 Subject: [PATCH 014/131] :lipstick: --- .../common/editor/textEditorModel.ts | 28 +++++-------------- .../textfile/common/textFileEditorModel.ts | 11 ++++++++ 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/common/editor/textEditorModel.ts b/src/vs/workbench/common/editor/textEditorModel.ts index 56c18f7f7fb..c6e90d4abbd 100644 --- a/src/vs/workbench/common/editor/textEditorModel.ts +++ b/src/vs/workbench/common/editor/textEditorModel.ts @@ -24,8 +24,8 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd private modelDisposeListener: IDisposable; constructor( - @IModelService private modelService: IModelService, - @IModeService private modeService: IModeService, + @IModelService protected modelService: IModelService, + @IModeService protected modeService: IModeService, textEditorModelHandle?: URI ) { super(); @@ -100,7 +100,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd return this; } - private getFirstLineText(value: string | IRawText): string { + protected getFirstLineText(value: string | IRawText): string { if (typeof value === 'string') { const firstLineText = value.substr(0, 100); @@ -153,20 +153,6 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd this.textEditorModel.setValueFromRawText(rawText); } - /** - * Updates the text editor model mode based on the settings and configuration. - */ - protected updateTextEditorModelMode(modeId?: string): void { - if (!this.textEditorModel) { - return; - } - - const firstLineText = this.getFirstLineText(this.textEditorModel.getValue()); - const mode = this.getOrCreateMode(this.modeService, modeId, firstLineText); - - this.modelService.setMode(this.textEditorModel, mode); - } - /** * Returns the textual value of this editor model or null if it has not yet been created. */ @@ -179,6 +165,10 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd return null; } + public isResolved(): boolean { + return !!this.textEditorModelHandle; + } + public dispose(): void { if (this.modelDisposeListener) { this.modelDisposeListener.dispose(); // dispose this first because it will trigger another dispose() otherwise @@ -194,8 +184,4 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd super.dispose(); } - - public isResolved(): boolean { - return !!this.textEditorModelHandle; - } } \ No newline at end of file diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 811cd656b2e..76121a70a28 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -131,6 +131,17 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.updateTextEditorModelMode(); } + private updateTextEditorModelMode(modeId?: string): void { + if (!this.textEditorModel) { + return; + } + + const firstLineText = this.getFirstLineText(this.textEditorModel.getValue()); + const mode = this.getOrCreateMode(this.modeService, modeId, firstLineText); + + this.modelService.setMode(this.textEditorModel, mode); + } + public get onDidContentChange(): Event { return this._onDidContentChange.event; } From 3f18825cfa5e818e96623e93fbe580d9b2648254 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 29 Dec 2016 11:16:58 +0100 Subject: [PATCH 015/131] handle openFilesInNewWindow properly on main side --- src/vs/code/electron-main/windows.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index e822db3ad2d..2706160b11f 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -358,9 +358,9 @@ export class WindowsManager implements IWindowsMainService { openFilesInNewWindow = true; } else { openFilesInNewWindow = openConfig.preferNewWindow; - if (openFilesInNewWindow && !openConfig.cli.extensionDevelopmentPath) { // can be overriden via settings (not for PDE though!) + if (openFilesInNewWindow && !openConfig.cli.extensionDevelopmentPath) { // can be overriden via settings (not for extension development though!) const windowConfig = this.configurationService.getConfiguration('window'); - if (windowConfig && !windowConfig.openFilesInNewWindow) { + if (windowConfig && windowConfig.openFilesInNewWindow === false) { openFilesInNewWindow = false; // do not open in new window if user configured this explicitly } } From 2c1035ca09317d75ae343b012730b559f8e54b57 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 29 Dec 2016 11:47:58 +0100 Subject: [PATCH 016/131] better handling of openFilesInNewWindow setting --- src/vs/code/electron-main/windows.ts | 13 ++++--------- .../workbench/electron-browser/main.contribution.ts | 2 +- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 2706160b11f..1d7ec46461f 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -352,18 +352,13 @@ export class WindowsManager implements IWindowsMainService { // Handle files to open/diff or to create when we dont open a folder if (!foldersToOpen.length && (filesToOpen.length > 0 || filesToCreate.length > 0 || filesToDiff.length > 0)) { - // const the user settings override how files are open in a new window or same window unless we are forced + // let the user settings override how files are open in a new window or same window unless we are forced (not for extension development though) let openFilesInNewWindow: boolean; if (openConfig.forceNewWindow) { openFilesInNewWindow = true; - } else { - openFilesInNewWindow = openConfig.preferNewWindow; - if (openFilesInNewWindow && !openConfig.cli.extensionDevelopmentPath) { // can be overriden via settings (not for extension development though!) - const windowConfig = this.configurationService.getConfiguration('window'); - if (windowConfig && windowConfig.openFilesInNewWindow === false) { - openFilesInNewWindow = false; // do not open in new window if user configured this explicitly - } - } + } else if (!openConfig.cli.extensionDevelopmentPath) { + const windowConfig = this.configurationService.getConfiguration('window'); + openFilesInNewWindow = windowConfig && windowConfig.openFilesInNewWindow === true; } // Open Files in last instance if any and flag tells us so diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 8439146a057..8144e77924f 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -172,7 +172,7 @@ configurationRegistry.registerConfiguration({ let properties: { [path: string]: IJSONSchema; } = { 'window.openFilesInNewWindow': { 'type': 'boolean', - 'default': true, + 'default': false, 'description': nls.localize('openFilesInNewWindow', "When enabled, will open files in a new window instead of reusing an existing instance.") }, 'window.reopenFolders': { From c295af578e3f3d398c8d0319a1d454b5f81fd9b2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 29 Dec 2016 13:19:32 +0100 Subject: [PATCH 017/131] allow to force new window for File > Open when pressing modifier key (for #371) --- src/vs/code/electron-main/menus.ts | 25 +++++++++++++------ .../electron-browser/electronFileActions.ts | 7 +----- .../files.electron.contribution.ts | 12 +++++++++ 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 9a89145c64f..88106e5131d 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -356,14 +356,14 @@ export class VSCodeMenu { newFile = this.createMenuItem(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File"), 'workbench.action.files.newUntitledFile'); } - const open = new MenuItem(this.likeAction('workbench.action.files.openFileFolder', { label: mnemonicLabel(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")), click: () => this.windowsService.openFileFolderPicker() })); - const openFolder = new MenuItem(this.likeAction('workbench.action.files.openFolder', { label: mnemonicLabel(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")), click: () => this.windowsService.openFolderPicker() })); + const open = new MenuItem(this.likeAction('workbench.action.files.openFileFolder', { label: mnemonicLabel(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")), click: (menuItem, win, event) => this.windowsService.openFileFolderPicker(this.isOptionClick(event)) })); + const openFolder = new MenuItem(this.likeAction('workbench.action.files.openFolder', { label: mnemonicLabel(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")), click: (menuItem, win, event) => this.windowsService.openFolderPicker(this.isOptionClick(event)) })); let openFile: Electron.MenuItem; if (hasNoWindows) { - openFile = new MenuItem(this.likeAction('workbench.action.files.openFile', { label: mnemonicLabel(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")), click: () => this.windowsService.openFilePicker() })); + openFile = new MenuItem(this.likeAction('workbench.action.files.openFile', { label: mnemonicLabel(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")), click: (menuItem, win, event) => this.windowsService.openFilePicker(this.isOptionClick(event)) })); } else { - openFile = this.createMenuItem(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File..."), 'workbench.action.files.openFile'); + openFile = this.createMenuItem(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File..."), ['workbench.action.files.openFile', 'workbench.action.files.openFileInNewWindow']); } const openRecentMenu = new Menu(); @@ -470,7 +470,7 @@ export class VSCodeMenu { private createOpenRecentMenuItem(path: string, actionId: string): Electron.MenuItem { return new MenuItem(this.likeAction(actionId, { label: unMnemonicLabel(path), click: (menuItem, win, event) => { - const openInNewWindow = event && ((!platform.isMacintosh && event.ctrlKey) || (platform.isMacintosh && event.metaKey)); + const openInNewWindow = this.isOptionClick(event); const success = !!this.windowsService.open({ cli: this.environmentService.args, pathsToOpen: [path], forceNewWindow: openInNewWindow }); if (!success) { this.windowsService.removeFromRecentPathsList(path); @@ -479,6 +479,10 @@ export class VSCodeMenu { }, false)); } + private isOptionClick(event: Electron.Event): boolean { + return event && ((!platform.isMacintosh && (event.ctrlKey || event.shiftKey)) || (platform.isMacintosh && (event.metaKey || event.altKey))); + } + private createRoleMenuItem(label: string, actionId: string, role: Electron.MenuItemRole): Electron.MenuItem { const options: Electron.MenuItemOptions = { label: mnemonicLabel(label), @@ -890,11 +894,18 @@ export class VSCodeMenu { } } - private createMenuItem(label: string, actionId: string, enabled?: boolean, checked?: boolean): Electron.MenuItem; + private createMenuItem(label: string, actionId: string | string[], enabled?: boolean, checked?: boolean): Electron.MenuItem; private createMenuItem(label: string, click: () => void, enabled?: boolean, checked?: boolean): Electron.MenuItem; private createMenuItem(arg1: string, arg2: any, arg3?: boolean, arg4?: boolean): Electron.MenuItem { const label = mnemonicLabel(arg1); - const click: () => void = (typeof arg2 === 'function') ? arg2 : () => this.windowsService.sendToFocused('vscode:runAction', arg2); + const click: () => void = (typeof arg2 === 'function') ? arg2 : (menuItem, win, event) => { + let actionId = arg2; + if (Array.isArray(arg2)) { + actionId = this.isOptionClick(event) ? arg2[1] : arg2[0]; // support alternative action if we got multiple action Ids and the option key was pressed while invoking + } + + this.windowsService.sendToFocused('vscode:runAction', actionId); + }; const enabled = typeof arg3 === 'boolean' ? arg3 : this.windowsService.getWindowCount() > 0; const checked = typeof arg4 === 'boolean' ? arg4 : false; diff --git a/src/vs/workbench/parts/files/electron-browser/electronFileActions.ts b/src/vs/workbench/parts/files/electron-browser/electronFileActions.ts index 07a8345e710..53014f290b0 100644 --- a/src/vs/workbench/parts/files/electron-browser/electronFileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/electronFileActions.ts @@ -132,12 +132,7 @@ export class OpenFileAction extends Action { run(): TPromise { const fileResource = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' }); - // Handle in browser process - if (fileResource) { - return this.windowService.openFilePicker(false, paths.dirname(fileResource.fsPath)); - } - - return this.windowService.openFilePicker(); + return this.windowService.openFilePicker(false, fileResource ? paths.dirname(fileResource.fsPath) : void 0); } } diff --git a/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts b/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts index 36ae2efab2c..bf7b04b24f5 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts @@ -22,6 +22,9 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; +import { toResource } from 'vs/workbench/common/editor'; +import paths = require('vs/base/common/paths'); +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; class FileViewerActionContributor extends ActionBarContributor { @@ -90,4 +93,13 @@ CommandsRegistry.registerCommand('_files.openFolderPicker', (accessor: ServicesA CommandsRegistry.registerCommand('_files.windowOpen', (accessor: ServicesAccessor, paths: string[], forceNewWindow: boolean) => { const windowsService = accessor.get(IWindowsService); windowsService.windowOpen(paths, forceNewWindow); +}); + +CommandsRegistry.registerCommand('workbench.action.files.openFileInNewWindow', (accessor: ServicesAccessor) => { + const windowService = accessor.get(IWindowService); + const editorService = accessor.get(IWorkbenchEditorService); + + const fileResource = toResource(editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' }); + + return windowService.openFilePicker(true, fileResource ? paths.dirname(fileResource.fsPath) : void 0); }); \ No newline at end of file From 53b74c12204bd427e05c88446de4f4e42dd59504 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 29 Dec 2016 15:11:29 +0100 Subject: [PATCH 018/131] shorten paths in main menu by looking for userHome --- src/vs/code/electron-main/menus.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 88106e5131d..e73e99d3dee 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -468,6 +468,10 @@ export class VSCodeMenu { } private createOpenRecentMenuItem(path: string, actionId: string): Electron.MenuItem { + if ((platform.isMacintosh || platform.isLinux) && path.indexOf(this.environmentService.userHome) === 0) { + path = `~${path.substr(this.environmentService.userHome.length)}`; + } + return new MenuItem(this.likeAction(actionId, { label: unMnemonicLabel(path), click: (menuItem, win, event) => { const openInNewWindow = this.isOptionClick(event); From f814a113b9b19b149cae50b0921b86fc03d55f43 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 29 Dec 2016 15:46:51 +0100 Subject: [PATCH 019/131] :lipstick: --- src/vs/code/electron-main/windows.ts | 16 ++++++++-------- .../windows/electron-main/windowsService.ts | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 1d7ec46461f..22505a155b8 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -347,7 +347,7 @@ export class WindowsManager implements IWindowsMainService { filesToOpen = candidates; } - let openInNewWindow = openConfig.preferNewWindow || openConfig.forceNewWindow; + let openFolderInNewWindow = openConfig.preferNewWindow || openConfig.forceNewWindow; // Handle files to open/diff or to create when we dont open a folder if (!foldersToOpen.length && (filesToOpen.length > 0 || filesToCreate.length > 0 || filesToDiff.length > 0)) { @@ -378,7 +378,7 @@ export class WindowsManager implements IWindowsMainService { const browserWindow = this.openInBrowserWindow(configuration, true /* new window */); usedWindows.push(browserWindow); - openInNewWindow = true; // any other folders to open must open in new window then + openFolderInNewWindow = true; // any other folders to open must open in new window then } // Reset these because we handled them @@ -407,7 +407,7 @@ export class WindowsManager implements IWindowsMainService { filesToCreate = []; filesToDiff = []; - openInNewWindow = true; // any other folders to open must open in new window then + openFolderInNewWindow = true; // any other folders to open must open in new window then } // Open remaining ones @@ -417,7 +417,7 @@ export class WindowsManager implements IWindowsMainService { } const configuration = this.toConfiguration(openConfig, folderToOpen, filesToOpen, filesToCreate, filesToDiff); - const browserWindow = this.openInBrowserWindow(configuration, openInNewWindow, openInNewWindow ? void 0 : openConfig.windowToUse); + const browserWindow = this.openInBrowserWindow(configuration, openFolderInNewWindow, openFolderInNewWindow ? void 0 : openConfig.windowToUse); usedWindows.push(browserWindow); // Reset these because we handled them @@ -425,7 +425,7 @@ export class WindowsManager implements IWindowsMainService { filesToCreate = []; filesToDiff = []; - openInNewWindow = true; // any other folders to open must open in new window then + openFolderInNewWindow = true; // any other folders to open must open in new window then }); } @@ -436,7 +436,7 @@ export class WindowsManager implements IWindowsMainService { const browserWindow = this.openInBrowserWindow(configuration, true /* new window */, null, emptyWorkspaceBackupFolder); usedWindows.push(browserWindow); - openInNewWindow = true; // any other folders to open must open in new window then + openFolderInNewWindow = true; // any other folders to open must open in new window then }); } @@ -444,10 +444,10 @@ export class WindowsManager implements IWindowsMainService { else if (emptyToOpen.length > 0) { emptyToOpen.forEach(() => { const configuration = this.toConfiguration(openConfig); - const browserWindow = this.openInBrowserWindow(configuration, openInNewWindow, openInNewWindow ? void 0 : openConfig.windowToUse); + const browserWindow = this.openInBrowserWindow(configuration, openFolderInNewWindow, openFolderInNewWindow ? void 0 : openConfig.windowToUse); usedWindows.push(browserWindow); - openInNewWindow = true; // any other folders to open must open in new window then + openFolderInNewWindow = true; // any other folders to open must open in new window then }); } diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 8209ff53b5a..e9621c7cf6e 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -203,7 +203,7 @@ export class WindowsService implements IWindowsService, IDisposable { return TPromise.as(null); } - this.windowsMainService.open({ cli: this.environmentService.args, pathsToOpen: paths, forceNewWindow: forceNewWindow }); + this.windowsMainService.open({ cli: this.environmentService.args, pathsToOpen: paths, forceNewWindow }); return TPromise.as(null); } From af7448297a40963401ae0caedf07514cd09c0e68 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 29 Dec 2016 16:27:23 +0100 Subject: [PATCH 020/131] add a context to the windows.open() method to better understand how opening was triggered --- src/vs/code/electron-main/launch.ts | 10 +++-- src/vs/code/electron-main/main.ts | 9 +++-- src/vs/code/electron-main/menus.ts | 10 ++--- src/vs/code/electron-main/windows.ts | 39 ++++++++++++++----- .../windows/electron-main/windowsService.ts | 10 ++--- 5 files changed, 50 insertions(+), 28 deletions(-) diff --git a/src/vs/code/electron-main/launch.ts b/src/vs/code/electron-main/launch.ts index 554207d24aa..52bbf4ccb86 100644 --- a/src/vs/code/electron-main/launch.ts +++ b/src/vs/code/electron-main/launch.ts @@ -5,7 +5,7 @@ 'use strict'; -import { IWindowsMainService } from 'vs/code/electron-main/windows'; +import { IWindowsMainService, OpenContext } from 'vs/code/electron-main/windows'; import { VSCodeWindow } from 'vs/code/electron-main/window'; import { TPromise } from 'vs/base/common/winjs.base'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; @@ -82,6 +82,7 @@ export class LaunchService implements ILaunchService { const openUrlArg = args['open-url'] || []; const openUrl = typeof openUrlArg === 'string' ? [openUrlArg] : openUrlArg; + const context = !!userEnv['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.OTHER; if (openUrl.length > 0) { openUrl.forEach(url => this.urlService.open(url)); @@ -91,13 +92,14 @@ export class LaunchService implements ILaunchService { // Otherwise handle in windows service let usedWindows: VSCodeWindow[]; if (!!args.extensionDevelopmentPath) { - this.windowsService.openExtensionDevelopmentHostWindow({ cli: args, userEnv }); + this.windowsService.openExtensionDevelopmentHostWindow({ context, cli: args, userEnv }); } else if (args._.length === 0 && args['new-window']) { - usedWindows = this.windowsService.open({ cli: args, userEnv, forceNewWindow: true, forceEmpty: true }); + usedWindows = this.windowsService.open({ context, cli: args, userEnv, forceNewWindow: true, forceEmpty: true }); } else if (args._.length === 0) { - usedWindows = [this.windowsService.focusLastActive(args)]; + usedWindows = [this.windowsService.focusLastActive(args, context)]; } else { usedWindows = this.windowsService.open({ + context, cli: args, userEnv, forceNewWindow: args.wait || args['new-window'], diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 0accc78830e..b9e11a0702a 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -11,7 +11,7 @@ import * as platform from 'vs/base/common/platform'; import { parseMainProcessArgv } from 'vs/platform/environment/node/argv'; import { mkdirp } from 'vs/base/node/pfs'; import { validatePaths } from 'vs/code/electron-main/paths'; -import { IWindowsMainService, WindowsManager } from 'vs/code/electron-main/windows'; +import { IWindowsMainService, WindowsManager, OpenContext } from 'vs/code/electron-main/windows'; import { IWindowsService } from 'vs/platform/windows/common/windows'; import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc'; import { WindowsService } from 'vs/platform/windows/electron-main/windowsService'; @@ -251,12 +251,13 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo windowsMainService.ready(userEnv); // Open our first window + const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.OTHER; if (environmentService.args['new-window'] && environmentService.args._.length === 0) { - windowsMainService.open({ cli: environmentService.args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths + windowsMainService.open({ context, cli: environmentService.args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths } else if (global.macOpenFiles && global.macOpenFiles.length && (!environmentService.args._ || !environmentService.args._.length)) { - windowsMainService.open({ cli: environmentService.args, pathsToOpen: global.macOpenFiles, initialStartup: true }); // mac: open-file event received on startup + windowsMainService.open({ context: OpenContext.DOCK, cli: environmentService.args, pathsToOpen: global.macOpenFiles, initialStartup: true }); // mac: open-file event received on startup } else { - windowsMainService.open({ cli: environmentService.args, forceNewWindow: environmentService.args['new-window'], diffMode: environmentService.args.diff, initialStartup: true }); // default: read paths from cli + windowsMainService.open({ context, cli: environmentService.args, forceNewWindow: environmentService.args['new-window'], diffMode: environmentService.args.diff, initialStartup: true }); // default: read paths from cli } // Install Menu diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index e73e99d3dee..cdfb3236efb 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -10,7 +10,7 @@ import * as platform from 'vs/base/common/platform'; import * as arrays from 'vs/base/common/arrays'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ipcMain as ipc, app, shell, dialog, Menu, MenuItem } from 'electron'; -import { IWindowsMainService } from 'vs/code/electron-main/windows'; +import { IWindowsMainService, OpenContext } from 'vs/code/electron-main/windows'; import { VSCodeWindow } from 'vs/code/electron-main/window'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageService } from 'vs/code/electron-main/storage'; @@ -315,7 +315,7 @@ export class VSCodeMenu { this.appMenuInstalled = true; const dockMenu = new Menu(); - dockMenu.append(new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow() })); + dockMenu.append(new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow(OpenContext.DOCK) })); app.dock.setMenu(dockMenu); } @@ -351,7 +351,7 @@ export class VSCodeMenu { let newFile: Electron.MenuItem; if (hasNoWindows) { - newFile = new MenuItem(this.likeAction('workbench.action.files.newUntitledFile', { label: mnemonicLabel(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File")), click: () => this.windowsService.openNewWindow() })); + newFile = new MenuItem(this.likeAction('workbench.action.files.newUntitledFile', { label: mnemonicLabel(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File")), click: () => this.windowsService.openNewWindow(OpenContext.MENU) })); } else { newFile = this.createMenuItem(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File"), 'workbench.action.files.newUntitledFile'); } @@ -379,7 +379,7 @@ export class VSCodeMenu { const preferences = this.getPreferencesMenu(); - const newWindow = new MenuItem(this.likeAction('workbench.action.newWindow', { label: mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow() })); + const newWindow = new MenuItem(this.likeAction('workbench.action.newWindow', { label: mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow(OpenContext.MENU) })); const revertFile = this.createMenuItem(nls.localize({ key: 'miRevert', comment: ['&& denotes a mnemonic'] }, "Re&&vert File"), 'workbench.action.files.revert', this.windowsService.getWindowCount() > 0); const closeWindow = new MenuItem(this.likeAction('workbench.action.closeWindow', { label: mnemonicLabel(nls.localize({ key: 'miCloseWindow', comment: ['&& denotes a mnemonic'] }, "Clos&&e Window")), click: () => this.windowsService.getLastActiveWindow().win.close(), enabled: this.windowsService.getWindowCount() > 0 })); @@ -475,7 +475,7 @@ export class VSCodeMenu { return new MenuItem(this.likeAction(actionId, { label: unMnemonicLabel(path), click: (menuItem, win, event) => { const openInNewWindow = this.isOptionClick(event); - const success = !!this.windowsService.open({ cli: this.environmentService.args, pathsToOpen: [path], forceNewWindow: openInNewWindow }); + const success = !!this.windowsService.open({ context: OpenContext.MENU, cli: this.environmentService.args, pathsToOpen: [path], forceNewWindow: openInNewWindow }); if (!success) { this.windowsService.removeFromRecentPathsList(path); } diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 22505a155b8..1d044bdd7fd 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -35,7 +35,26 @@ enum WindowError { CRASHED } +export enum OpenContext { + + // opening when running from the command line + CLI, + + // macOS only: opening from the dock (also when opening files to a running instance from desktop) + DOCK, + + // opening from the main application window + MENU, + + // opening from a file or folder dialog + DIALOG, + + // any other way of opening + OTHER +} + export interface IOpenConfiguration { + context: OpenContext; cli: ParsedArgs; userEnv?: platform.IProcessEnvironment; pathsToOpen?: string[]; @@ -97,10 +116,10 @@ export interface IWindowsMainService { openFilePicker(forceNewWindow?: boolean, path?: string, window?: VSCodeWindow): void; openFolderPicker(forceNewWindow?: boolean, window?: VSCodeWindow): void; openAccessibilityOptions(): void; - focusLastActive(cli: ParsedArgs): VSCodeWindow; + focusLastActive(cli: ParsedArgs, context: OpenContext): VSCodeWindow; getLastActiveWindow(): VSCodeWindow; findWindow(workspacePath: string, filePath?: string, extensionDevelopmentPath?: string): VSCodeWindow; - openNewWindow(): void; + openNewWindow(context: OpenContext): void; sendToFocused(channel: string, ...args: any[]): void; sendToAll(channel: string, payload: any, windowIdsToIgnore?: number[]): void; getFocusedWindow(): VSCodeWindow; @@ -168,7 +187,7 @@ export class WindowsManager implements IWindowsMainService { // Mac only event: open new window when we get activated if (!hasVisibleWindows) { - this.openNewWindow(); + this.openNewWindow(OpenContext.DOCK); } }); @@ -189,7 +208,7 @@ export class WindowsManager implements IWindowsMainService { // Handle paths delayed in case more are coming! runningTimeout = setTimeout(() => { - this.open({ cli: this.environmentService.args, pathsToOpen: macOpenFiles, preferNewWindow: true /* dropping on the dock prefers to open in a new window */ }); + this.open({ context: OpenContext.DOCK, cli: this.environmentService.args, pathsToOpen: macOpenFiles, preferNewWindow: true /* dropping on the dock prefers to open in a new window */ }); macOpenFiles = []; runningTimeout = null; }, 100); @@ -606,7 +625,7 @@ export class WindowsManager implements IWindowsMainService { } // Open it - this.open({ cli: openConfig.cli, forceNewWindow: true, forceEmpty: openConfig.cli._.length === 0 }); + this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: openConfig.cli._.length === 0 }); } private toConfiguration(config: IOpenConfiguration, workspacePath?: string, filesToOpen?: IPath[], filesToCreate?: IPath[], filesToDiff?: IPath[]): IWindowConfiguration { @@ -887,7 +906,7 @@ export class WindowsManager implements IWindowsMainService { private doPickAndOpen(options: INativeOpenDialogOptions): void { this.getFileOrFolderPaths(options, (paths: string[]) => { if (paths && paths.length) { - this.open({ cli: this.environmentService.args, pathsToOpen: paths, forceNewWindow: options.forceNewWindow }); + this.open({ context: OpenContext.DIALOG, cli: this.environmentService.args, pathsToOpen: paths, forceNewWindow: options.forceNewWindow }); } }); } @@ -920,7 +939,7 @@ export class WindowsManager implements IWindowsMainService { }); } - public focusLastActive(cli: ParsedArgs): VSCodeWindow { + public focusLastActive(cli: ParsedArgs, context: OpenContext): VSCodeWindow { const lastActive = this.getLastActiveWindow(); if (lastActive) { lastActive.focus(); @@ -930,7 +949,7 @@ export class WindowsManager implements IWindowsMainService { // No window - open new one this.windowsState.openedFolders = []; // make sure we do not open too much - const res = this.open({ cli: cli }); + const res = this.open({ context, cli }); return res && res[0]; } @@ -992,8 +1011,8 @@ export class WindowsManager implements IWindowsMainService { return null; } - public openNewWindow(): void { - this.open({ cli: this.environmentService.args, forceNewWindow: true, forceEmpty: true }); + public openNewWindow(context: OpenContext): void { + this.open({ context, cli: this.environmentService.args, forceNewWindow: true, forceEmpty: true }); } public sendToFocused(channel: string, ...args: any[]): void { diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index e9621c7cf6e..aa9adf73444 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -16,7 +16,7 @@ import { fromEventEmitter } from 'vs/base/node/event'; import { IURLService } from 'vs/platform/url/common/url'; // TODO@Joao: remove this dependency, move all implementation to this class -import { IWindowsMainService } from 'vs/code/electron-main/windows'; +import { IWindowsMainService, OpenContext } from 'vs/code/electron-main/windows'; export class WindowsService implements IWindowsService, IDisposable { @@ -94,7 +94,7 @@ export class WindowsService implements IWindowsService, IDisposable { const vscodeWindow = this.windowsMainService.getWindowById(windowId); if (vscodeWindow) { - this.windowsMainService.open({ cli: this.environmentService.args, forceEmpty: true, windowToUse: vscodeWindow }); + this.windowsMainService.open({ context: OpenContext.OTHER, cli: this.environmentService.args, forceEmpty: true, windowToUse: vscodeWindow }); } return TPromise.as(null); @@ -203,12 +203,12 @@ export class WindowsService implements IWindowsService, IDisposable { return TPromise.as(null); } - this.windowsMainService.open({ cli: this.environmentService.args, pathsToOpen: paths, forceNewWindow }); + this.windowsMainService.open({ context: OpenContext.OTHER, cli: this.environmentService.args, pathsToOpen: paths, forceNewWindow }); return TPromise.as(null); } openNewWindow(): TPromise { - this.windowsMainService.openNewWindow(); + this.windowsMainService.openNewWindow(OpenContext.OTHER); return TPromise.as(null); } @@ -271,7 +271,7 @@ export class WindowsService implements IWindowsService, IDisposable { const cli = assign(Object.create(null), this.environmentService.args, { goto: true }); const pathsToOpen = [filePath]; - this.windowsMainService.open({ cli, pathsToOpen }); + this.windowsMainService.open({ context: OpenContext.OTHER, cli, pathsToOpen }); return TPromise.as(null); } From fbe8445fdb622ee707e9bd135fff930d20037476 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 29 Dec 2016 18:29:27 +0100 Subject: [PATCH 021/131] set files to open as pinned when opened on startup --- src/vs/workbench/electron-browser/window.ts | 2 +- src/vs/workbench/electron-browser/workbench.ts | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 51a0464b43b..012c423cb3a 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -380,7 +380,7 @@ export class ElectronWindow { // In diffMode we open 2 resources as diff if (diffMode && resources.length === 2) { - return this.editorService.openEditor({ leftResource: resources[0].resource, rightResource: resources[1].resource }); + return this.editorService.openEditor({ leftResource: resources[0].resource, rightResource: resources[1].resource, options: { pinned: true } }); } // For one file, just put it into the current active editor diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index d483cf7d956..10e91fab40a 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -357,7 +357,7 @@ export class Workbench implements IPartService { // Files to diff is exclusive if (filesToDiff && filesToDiff.length === 2) { - return this.editorService.createInput({ leftResource: filesToDiff[0].resource, rightResource: filesToDiff[1].resource }).then(input => [{ input }]); + return this.editorService.createInput({ leftResource: filesToDiff[0].resource, rightResource: filesToDiff[1].resource }).then(input => [{ input, options: EditorOptions.create({ pinned: true }) }]); } // Otherwise: Open/Create files @@ -367,14 +367,19 @@ export class Workbench implements IPartService { // Files to create inputs.push(...filesToCreate.map(resourceInput => this.untitledEditorService.createOrGet(resourceInput.resource))); - options.push(...filesToCreate.map(r => null)); // fill empty options for files to create because we dont have options there + options.push(...filesToCreate.map(r => EditorOptions.create({ pinned: true }))); // Files to open let filesToOpenInputPromise = filesToOpen.map(resourceInput => this.editorService.createInput(resourceInput)); return TPromise.join(filesToOpenInputPromise).then((inputsToOpen) => { inputs.push(...inputsToOpen); - options.push(...filesToOpen.map(resourceInput => TextEditorOptions.from(resourceInput))); + options.push(...filesToOpen.map(resourceInput => { + const options: EditorOptions = TextEditorOptions.from(resourceInput) || EditorOptions.create({ pinned: true }); + options.pinned = true; + + return options; + })); return inputs.map((input, index) => { return { input, options: options[index] }; }); }); From bf6413187d0a8f81eec84f08b901e2b752989f1d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 29 Dec 2016 18:37:34 +0100 Subject: [PATCH 022/131] bug: multiple files no longer open in same window --- src/vs/code/electron-main/windows.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 1d044bdd7fd..fd360bfda6f 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -384,8 +384,9 @@ export class WindowsManager implements IWindowsMainService { const lastActiveWindow = this.getLastActiveWindow(); if (!openFilesInNewWindow && lastActiveWindow) { lastActiveWindow.focus(); + const files = { filesToOpen, filesToCreate, filesToDiff }; // copy to object because they get reset shortly after lastActiveWindow.ready().then(readyWindow => { - readyWindow.send('vscode:openFiles', { filesToOpen, filesToCreate, filesToDiff }); + readyWindow.send('vscode:openFiles', files); }); usedWindows.push(lastActiveWindow); @@ -415,8 +416,9 @@ export class WindowsManager implements IWindowsMainService { if (windowsOnWorkspacePath.length > 0) { const browserWindow = windowsOnWorkspacePath[0]; browserWindow.focus(); // just focus one of them + const files = { filesToOpen, filesToCreate, filesToDiff }; // copy to object because they get reset shortly after browserWindow.ready().then(readyWindow => { - readyWindow.send('vscode:openFiles', { filesToOpen, filesToCreate, filesToDiff }); + readyWindow.send('vscode:openFiles', files); }); usedWindows.push(browserWindow); From 752b43ef7948e926cdf56902b7fbae9955151c0c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 30 Dec 2016 08:26:11 +0100 Subject: [PATCH 023/131] fix issue in recent menu with truncated paths --- src/vs/code/electron-main/menus.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index cdfb3236efb..7bfc3bc7ec3 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -468,12 +468,13 @@ export class VSCodeMenu { } private createOpenRecentMenuItem(path: string, actionId: string): Electron.MenuItem { + let label = path; if ((platform.isMacintosh || platform.isLinux) && path.indexOf(this.environmentService.userHome) === 0) { - path = `~${path.substr(this.environmentService.userHome.length)}`; + label = `~${path.substr(this.environmentService.userHome.length)}`; } return new MenuItem(this.likeAction(actionId, { - label: unMnemonicLabel(path), click: (menuItem, win, event) => { + label: unMnemonicLabel(label), click: (menuItem, win, event) => { const openInNewWindow = this.isOptionClick(event); const success = !!this.windowsService.open({ context: OpenContext.MENU, cli: this.environmentService.args, pathsToOpen: [path], forceNewWindow: openInNewWindow }); if (!success) { From 6ea36fe32049c4e99412468f59716befbd064e59 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 30 Dec 2016 08:27:06 +0100 Subject: [PATCH 024/131] introduce window.openFoldersInNewWindow for #371 --- src/vs/code/electron-main/windows.ts | 15 +++++++++------ src/vs/platform/windows/common/windows.ts | 1 + .../electron-browser/main.contribution.ts | 5 +++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 38fc57bff90..91464f0eea2 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -291,6 +291,8 @@ export class WindowsManager implements IWindowsMainService { } public open(openConfig: IOpenConfiguration): VSCodeWindow[] { + const windowConfig = this.configurationService.getConfiguration('window'); + let iPathsToOpen: IPath[]; const usedWindows: VSCodeWindow[] = []; @@ -364,18 +366,19 @@ export class WindowsManager implements IWindowsMainService { filesToOpen = candidates; } + // let the user settings override how folders are open in a new window or same window unless we are forced let openFolderInNewWindow = openConfig.preferNewWindow || openConfig.forceNewWindow; + if (!openConfig.forceNewWindow && windowConfig && typeof windowConfig.openFoldersInNewWindow === 'boolean') { + openFolderInNewWindow = windowConfig.openFoldersInNewWindow; + } // Handle files to open/diff or to create when we dont open a folder if (!foldersToOpen.length && (filesToOpen.length > 0 || filesToCreate.length > 0 || filesToDiff.length > 0)) { // let the user settings override how files are open in a new window or same window unless we are forced (not for extension development though) - let openFilesInNewWindow: boolean; - if (openConfig.forceNewWindow) { - openFilesInNewWindow = true; - } else if (!openConfig.cli.extensionDevelopmentPath) { - const windowConfig = this.configurationService.getConfiguration('window'); - openFilesInNewWindow = windowConfig && windowConfig.openFilesInNewWindow === true; + let openFilesInNewWindow = openConfig.preferNewWindow || openConfig.forceNewWindow; + if (!openConfig.forceNewWindow && !openConfig.cli.extensionDevelopmentPath && windowConfig && typeof windowConfig.openFilesInNewWindow === 'boolean') { + openFilesInNewWindow = windowConfig.openFilesInNewWindow; } // Open Files in last instance if any and flag tells us so diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index ee7e6f481c5..000fd229a12 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -88,6 +88,7 @@ export interface IWindowService { export interface IWindowSettings { openFilesInNewWindow: boolean; + openFoldersInNewWindow: boolean; reopenFolders: 'all' | 'one' | 'none'; restoreFullscreen: boolean; zoomLevel: number; diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 8144e77924f..f619af561be 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -175,6 +175,11 @@ let properties: { [path: string]: IJSONSchema; } = { 'default': false, 'description': nls.localize('openFilesInNewWindow', "When enabled, will open files in a new window instead of reusing an existing instance.") }, + 'window.openFoldersInNewWindow': { + 'type': 'boolean', + 'default': false, + 'description': nls.localize('openFoldersInNewWindow', "When enabled, will open folders in a new window instead of reusing an existing instance.") + }, 'window.reopenFolders': { 'type': 'string', 'enum': ['none', 'one', 'all'], From 69c2d204f71b5a2b66814ed7a3fa3fc904977c33 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 30 Dec 2016 08:44:36 +0100 Subject: [PATCH 025/131] more tweaks to file open behaviour --- src/vs/code/electron-main/windows.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 91464f0eea2..29b8b21d27d 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -376,9 +376,17 @@ export class WindowsManager implements IWindowsMainService { if (!foldersToOpen.length && (filesToOpen.length > 0 || filesToCreate.length > 0 || filesToDiff.length > 0)) { // let the user settings override how files are open in a new window or same window unless we are forced (not for extension development though) - let openFilesInNewWindow = openConfig.preferNewWindow || openConfig.forceNewWindow; - if (!openConfig.forceNewWindow && !openConfig.cli.extensionDevelopmentPath && windowConfig && typeof windowConfig.openFilesInNewWindow === 'boolean') { - openFilesInNewWindow = windowConfig.openFilesInNewWindow; + let openFilesInNewWindow: boolean; + if (openConfig.forceNewWindow) { + openFilesInNewWindow = true; + } else { + if (openConfig.preferNewWindow && openConfig.context === OpenContext.DOCK) { + openFilesInNewWindow = true; // only on macOS do we allow to open files in a new window if this is triggered via DOCK context + } + + if (!openConfig.cli.extensionDevelopmentPath && windowConfig && typeof windowConfig.openFilesInNewWindow === 'boolean') { + openFilesInNewWindow = windowConfig.openFilesInNewWindow; + } } // Open Files in last instance if any and flag tells us so From 62094332dfc7e6f4315f90e39b7383ccaebe5017 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 30 Dec 2016 08:49:56 +0100 Subject: [PATCH 026/131] settings wording --- src/vs/workbench/electron-browser/main.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index f619af561be..ad735935c75 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -173,12 +173,12 @@ let properties: { [path: string]: IJSONSchema; } = { 'window.openFilesInNewWindow': { 'type': 'boolean', 'default': false, - 'description': nls.localize('openFilesInNewWindow', "When enabled, will open files in a new window instead of reusing an existing instance.") + 'description': nls.localize('openFilesInNewWindow', "When enabled, will prefer to open files in a new window instead of reusing an existing instance. Note that there can still be cases where this setting is ignored (e.g. when using the -n command line option).") }, 'window.openFoldersInNewWindow': { 'type': 'boolean', 'default': false, - 'description': nls.localize('openFoldersInNewWindow', "When enabled, will open folders in a new window instead of reusing an existing instance.") + 'description': nls.localize('openFoldersInNewWindow', "When enabled, will prefer to open folders in a new window instead of reusing an existing instance. Note that there can still be cases where this setting is ignored (e.g. when using the -n command line option).") }, 'window.reopenFolders': { 'type': 'string', From 7215ec283916d7ef55c3d7178e5b844e9e73556e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 30 Dec 2016 09:12:28 +0100 Subject: [PATCH 027/131] introduce and use forceReuseWindow flag --- src/vs/code/electron-main/launch.ts | 1 + src/vs/code/electron-main/windows.ts | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/code/electron-main/launch.ts b/src/vs/code/electron-main/launch.ts index 52bbf4ccb86..026fad8e55b 100644 --- a/src/vs/code/electron-main/launch.ts +++ b/src/vs/code/electron-main/launch.ts @@ -104,6 +104,7 @@ export class LaunchService implements ILaunchService { userEnv, forceNewWindow: args.wait || args['new-window'], preferNewWindow: !args['reuse-window'], + forceReuseWindow: args['reuse-window'], diffMode: args.diff }); } diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 29b8b21d27d..3ba7ff6e107 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -59,6 +59,7 @@ export interface IOpenConfiguration { pathsToOpen?: string[]; preferNewWindow?: boolean; forceNewWindow?: boolean; + forceReuseWindow?: boolean; forceEmpty?: boolean; windowToUse?: VSCodeWindow; diffMode?: boolean; @@ -367,8 +368,8 @@ export class WindowsManager implements IWindowsMainService { } // let the user settings override how folders are open in a new window or same window unless we are forced - let openFolderInNewWindow = openConfig.preferNewWindow || openConfig.forceNewWindow; - if (!openConfig.forceNewWindow && windowConfig && typeof windowConfig.openFoldersInNewWindow === 'boolean') { + let openFolderInNewWindow = (openConfig.preferNewWindow || openConfig.forceNewWindow) && !openConfig.forceReuseWindow; + if (!openConfig.forceNewWindow && !openConfig.forceReuseWindow && windowConfig && typeof windowConfig.openFoldersInNewWindow === 'boolean') { openFolderInNewWindow = windowConfig.openFoldersInNewWindow; } @@ -377,8 +378,8 @@ export class WindowsManager implements IWindowsMainService { // let the user settings override how files are open in a new window or same window unless we are forced (not for extension development though) let openFilesInNewWindow: boolean; - if (openConfig.forceNewWindow) { - openFilesInNewWindow = true; + if (openConfig.forceNewWindow || openConfig.forceReuseWindow) { + openFilesInNewWindow = openConfig.forceNewWindow && !openConfig.forceReuseWindow; } else { if (openConfig.preferNewWindow && openConfig.context === OpenContext.DOCK) { openFilesInNewWindow = true; // only on macOS do we allow to open files in a new window if this is triggered via DOCK context From f2c47075c8dd2f16d5ebdd6215e5f36361d5d5c4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 30 Dec 2016 19:28:08 +0100 Subject: [PATCH 028/131] tslint: less implicit any use --- src/vs/base/browser/builder.ts | 2 +- src/vs/base/browser/event.ts | 6 +++--- src/vs/base/browser/htmlContentRenderer.ts | 2 +- src/vs/base/browser/ui/list/listView.ts | 2 +- src/vs/base/browser/ui/list/listWidget.ts | 4 ++-- src/vs/base/common/async.ts | 2 +- src/vs/base/common/color.ts | 6 +++--- src/vs/base/common/comparers.ts | 4 ++-- src/vs/base/common/decorators.ts | 2 +- src/vs/base/common/diagnostics.ts | 2 +- src/vs/base/common/diff/diff2.ts | 4 ++-- src/vs/base/common/event.ts | 6 +++--- src/vs/base/common/filters.ts | 4 ++-- src/vs/base/common/json.ts | 2 +- src/vs/base/common/map.ts | 2 +- src/vs/base/common/platform.ts | 6 +++--- src/vs/base/node/zip.ts | 2 +- src/vs/platform/environment/node/argv.ts | 2 +- .../keybinding/common/keybindingsRegistry.ts | 2 +- .../api/node/mainThreadEditorsTracker.ts | 2 +- .../api/node/mainThreadTreeExplorers.ts | 2 +- .../workbench/api/node/mainThreadWorkspace.ts | 6 +++--- .../browser/parts/editor/editorGroupsControl.ts | 2 -- .../browser/parts/editor/tabsTitleControl.ts | 2 +- .../common/editor/editorStacksModel.ts | 2 +- .../electron-browser/debugEditorContribution.ts | 2 +- .../parts/debug/electron-browser/debugHover.ts | 6 +++--- .../parts/debug/electron-browser/repl.ts | 2 -- .../parts/debug/electron-browser/replViewer.ts | 2 +- .../explorers/browser/views/treeExplorerView.ts | 10 +--------- .../extensions/browser/extensionsActions.ts | 2 +- .../extensions/browser/extensionsQuickOpen.ts | 2 +- .../parts/files/browser/fileActions.ts | 1 - .../parts/files/browser/saveErrorHandler.ts | 1 - .../preferences/browser/preferencesEditor.ts | 6 +++--- .../parts/search/common/searchModel.ts | 2 +- .../electron-browser/snippetsTracker.ts | 2 -- .../electron-browser/terminalInstance.ts | 4 ++-- .../electron-browser/releaseNotesEditor.ts | 6 ------ .../parts/update/electron-browser/update.ts | 17 +++++++---------- .../configuration/node/configurationService.ts | 2 +- .../node/configurationEditingService.test.ts | 4 +--- .../node/configurationResolverService.ts | 2 +- .../services/search/node/textSearch.ts | 2 +- .../services/search/node/worker/searchWorker.ts | 2 +- .../services/telemetry/common/workspaceStats.ts | 5 +++-- .../services/textfile/common/textFileService.ts | 2 -- .../electron-browser/textFileService.ts | 4 +--- .../electron-browser/stylesContributions.ts | 4 ++-- .../themes/electron-browser/themeService.ts | 6 +++--- src/vs/workbench/test/workbenchTestServices.ts | 3 +-- 51 files changed, 74 insertions(+), 105 deletions(-) diff --git a/src/vs/base/browser/builder.ts b/src/vs/base/browser/builder.ts index fd1f4e809dd..4f586a24a2e 100644 --- a/src/vs/base/browser/builder.ts +++ b/src/vs/base/browser/builder.ts @@ -692,7 +692,7 @@ export class Builder implements IDisposable { } }; - return this.on(arg1, fn, listenerToUnbindContainer); + return this.on(arg1, fn, listenerToUnbindContainer, useCapture); } /** diff --git a/src/vs/base/browser/event.ts b/src/vs/base/browser/event.ts index 3cec88098ba..6da3585864e 100644 --- a/src/vs/base/browser/event.ts +++ b/src/vs/base/browser/event.ts @@ -114,14 +114,14 @@ export interface IDomEvent { (element: EventHandler, type: string, useCapture?: boolean): _Event; } -export const domEvent: IDomEvent = (element: EventHandler, type: string, useCapture?) => { +export const domEvent: IDomEvent = (element: EventHandler, type: string, useCapture?: boolean) => { const fn = e => emitter.fire(e); const emitter = new Emitter({ onFirstListenerAdd: () => { - element.addEventListener(type, fn); + element.addEventListener(type, fn, useCapture); }, onLastListenerRemove: () => { - element.removeEventListener(type, fn); + element.removeEventListener(type, fn, useCapture); } }); diff --git a/src/vs/base/browser/htmlContentRenderer.ts b/src/vs/base/browser/htmlContentRenderer.ts index 2239732614d..e06eedb4d6d 100644 --- a/src/vs/base/browser/htmlContentRenderer.ts +++ b/src/vs/base/browser/htmlContentRenderer.ts @@ -91,7 +91,7 @@ function _renderHtml(content: IHTMLContentElement, options: RenderOptions = {}): const renderer = new marked.Renderer(); renderer.image = (href: string, title: string, text: string) => { - let dimensions = []; + let dimensions: string[] = []; if (href) { const splitted = href.split('|').map(s => s.trim()); href = splitted[0]; diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index dc2ae29ed42..6fec843ae34 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -240,7 +240,7 @@ export class ListView implements IDisposable { return DOM.addDisposableListener(domNode, type, handler, useCapture); } - private fireScopedEvent(handler: (event: any) => void, index) { + private fireScopedEvent(handler: (event: any) => void, index: number) { if (index < 0) { return; } diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 36581beb633..f05b962354a 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -65,7 +65,7 @@ class Trait implements IDisposable { splice(start: number, deleteCount: number, insertCount: number): void { const diff = insertCount - deleteCount; const end = start + deleteCount; - const indexes = []; + const indexes: number[] = []; for (let index of indexes) { if (index >= start && index < end) { @@ -110,7 +110,7 @@ class Trait implements IDisposable { class FocusTrait extends Trait { - constructor(private getElementId: (number) => string) { + constructor(private getElementId: (number: number) => string) { super('focused'); } diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 55442230b02..b899d0f7350 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -402,7 +402,7 @@ export function sequence(promiseFactories: ITask>[]): TPromise(promiseFactories: ITask>[], shouldStop: (t: T) => boolean = t => !!t): TPromise { promiseFactories = [...promiseFactories.reverse()]; - const loop = () => { + const loop: () => TPromise = () => { if (promiseFactories.length === 0) { return TPromise.as(null); } diff --git a/src/vs/base/common/color.ts b/src/vs/base/common/color.ts index a532c64cc86..bbc24c4aa3b 100644 --- a/src/vs/base/common/color.ts +++ b/src/vs/base/common/color.ts @@ -65,12 +65,12 @@ function hsla2rgba(hsla: HSLA): RGBA { let s = Math.min(hsla.s, 1); let l = Math.min(hsla.l, 1); let a = hsla.a === void 0 ? hsla.a : 1; - let r, g, b; + let r: number, g: number, b: number; if (s === 0) { r = g = b = l; // achromatic } else { - let hue2rgb = function hue2rgb(p, q, t) { + let hue2rgb = function hue2rgb(p: number, q: number, t: number) { if (t < 0) { t += 1; } @@ -115,7 +115,7 @@ export class Color { * Returns the number in the set [0, 1]. O => Darkest Black. 1 => Lightest white. */ public getLuminosity(): number { - let luminosityFor = function (color): number { + let luminosityFor = function (color: number): number { let c = color / 255; return (c <= 0.03928) ? c / 12.92 : Math.pow(((c + 0.055) / 1.055), 2.4); }; diff --git a/src/vs/base/common/comparers.ts b/src/vs/base/common/comparers.ts index a90bc8f98a5..7a77f168718 100644 --- a/src/vs/base/common/comparers.ts +++ b/src/vs/base/common/comparers.ts @@ -83,8 +83,8 @@ export function compareByPrefix(one: string, other: string, lookFor: string): nu } export interface IScorableResourceAccessor { - getLabel(T): string; - getResourcePath(T): string; + getLabel(t: T): string; + getResourcePath(t: T): string; } export function compareByScore(elementA: T, elementB: T, accessor: IScorableResourceAccessor, lookFor: string, lookForNormalizedLower: string, scorerCache?: { [key: string]: number }): number { diff --git a/src/vs/base/common/decorators.ts b/src/vs/base/common/decorators.ts index d20f57e96c3..e7330957a46 100644 --- a/src/vs/base/common/decorators.ts +++ b/src/vs/base/common/decorators.ts @@ -23,7 +23,7 @@ export function memoize(target: any, key: string, descriptor: any) { const memoizeKey = `$memoize$${key}`; - descriptor[fnKey] = function (...args) { + descriptor[fnKey] = function (...args: any[]) { if (!this.hasOwnProperty(memoizeKey)) { Object.defineProperty(this, memoizeKey, { configurable: false, diff --git a/src/vs/base/common/diagnostics.ts b/src/vs/base/common/diagnostics.ts index dc866d18614..9c0e4f6df8f 100644 --- a/src/vs/base/common/diagnostics.ts +++ b/src/vs/base/common/diagnostics.ts @@ -19,7 +19,7 @@ globals.Monaco.Diagnostics = {}; var switches = globals.Monaco.Diagnostics; var map = {}; -var data = []; +var data: any[] = []; function fifo(array: any[], size: number) { while (array.length > size) { diff --git a/src/vs/base/common/diff/diff2.ts b/src/vs/base/common/diff/diff2.ts index c37b16e49e5..91a92308335 100644 --- a/src/vs/base/common/diff/diff2.ts +++ b/src/vs/base/common/diff/diff2.ts @@ -148,8 +148,8 @@ export class LcsDiff2 { // Construct the changes let i = 0; let j = 0; - let xChangeStart, yChangeStart; - let changes = []; + let xChangeStart: number, yChangeStart: number; + let changes: DiffChange[] = []; while (i < xLength && j < yLength) { if (this.resultX[i] && this.resultY[j]) { // No change diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 9f9d99f83d1..926667f2a24 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -218,7 +218,7 @@ export function once(event: Event): Event { } export function any(...events: Event[]): Event { - let listeners = []; + let listeners: IDisposable[] = []; const emitter = new Emitter({ onFirstListenerAdd() { @@ -297,7 +297,7 @@ export class EventBufferer { } bufferEvents(fn: () => void): void { - const buffer = []; + const buffer: Function[] = []; this.buffers.push(buffer); fn(); this.buffers.pop(); @@ -334,7 +334,7 @@ class ChainableEvent implements IChainableEvent { return new ChainableEvent(filterEvent(this._event, fn)); } - on(listener, thisArgs, disposables) { + on(listener, thisArgs, disposables: IDisposable[]) { return this._event(listener, thisArgs, disposables); } } diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index ad0bc3ef508..66e7daaa755 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -209,7 +209,7 @@ function analyzeCamelCaseWord(word: string): ICamelCaseAnalysis { } function isUpperCaseWord(analysis: ICamelCaseAnalysis): boolean { - const { upperPercent, lowerPercent, alphaPercent, numericPercent } = analysis; + const { upperPercent, lowerPercent } = analysis; return lowerPercent === 0 && upperPercent > 0.6; } @@ -298,7 +298,7 @@ function _matchesWords(word: string, target: string, i: number, j: number): IMat } else if (word[i] !== target[j].toLowerCase()) { return null; } else { - let result = null; + let result: IMatch[] = null; let nextWordIndex = j + 1; result = _matchesWords(word, target, i + 1, j + 1); while (!result && (nextWordIndex = nextWord(target, nextWordIndex)) < target.length) { diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts index 62ba0371429..a22159dfc85 100644 --- a/src/vs/base/common/json.ts +++ b/src/vs/base/common/json.ts @@ -42,7 +42,7 @@ export interface JSONScanner { /** * Sets the scan position to a new offset. A call to 'scan' is needed to get the first token. */ - setPosition(pos: number); + setPosition(pos: number): void; /** * Read the next token. Returns the tolen code. */ diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 2e57ca4cbc1..3906356d003 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -310,7 +310,7 @@ class Node { */ export class TrieMap { - static PathSplitter = s => s.split(/[\\/]/).filter(s => !!s); + static PathSplitter = (s: string) => s.split(/[\\/]/).filter(s => !!s); private _splitter: (s: string) => string[]; private _root = new Node(); diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index 3075beb0a97..3ba367b9b62 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -13,8 +13,8 @@ let _isRootUser = false; let _isNative = false; let _isWeb = false; let _isQunit = false; -let _locale = undefined; -let _language = undefined; +let _locale: string = undefined; +let _language: string = undefined; interface NLSConfig { locale: string; @@ -124,7 +124,7 @@ interface IGlobals { clearTimeout(token: TimeoutToken): void; setInterval(callback: (...args: any[]) => void, delay: number, ...args: any[]): IntervalToken; - clearInterval(token: IntervalToken); + clearInterval(token: IntervalToken): void; } const _globals = (typeof self === 'object' ? self : global); diff --git a/src/vs/base/node/zip.ts b/src/vs/base/node/zip.ts index 222149de148..aabb762c20e 100644 --- a/src/vs/base/node/zip.ts +++ b/src/vs/base/node/zip.ts @@ -106,7 +106,7 @@ function read(zipPath: string, filePath: string): TPromise { export function buffer(zipPath: string, filePath: string): TPromise { return read(zipPath, filePath).then(stream => { return new TPromise((c, e) => { - const buffers = []; + const buffers: Buffer[] = []; stream.once('error', e); stream.on('data', b => buffers.push(b)); stream.on('end', () => c(Buffer.concat(buffers))); diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 13e55e6f3fb..caa91b82bb4 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -148,7 +148,7 @@ export function formatOptions(options: { [name: string]: string; }, columns: num } function wrapText(text: string, columns: number): string[] { - let lines = []; + let lines: string[] = []; while (text.length) { let index = text.length < columns ? text.length : text.lastIndexOf(' ', columns); let line = text.slice(0, index).trim(); diff --git a/src/vs/platform/keybinding/common/keybindingsRegistry.ts b/src/vs/platform/keybinding/common/keybindingsRegistry.ts index 02d64285d52..ea1ef26e4ce 100644 --- a/src/vs/platform/keybinding/common/keybindingsRegistry.ts +++ b/src/vs/platform/keybinding/common/keybindingsRegistry.ts @@ -23,7 +23,7 @@ export interface ICommandAndKeybindingRule extends IKeybindingRule { } export interface IKeybindingsRegistry { - registerKeybindingRule(rule: IKeybindingRule); + registerKeybindingRule(rule: IKeybindingRule): void; registerCommandAndKeybindingRule(desc: ICommandAndKeybindingRule): void; getDefaultKeybindings(): IKeybindingItem[]; diff --git a/src/vs/workbench/api/node/mainThreadEditorsTracker.ts b/src/vs/workbench/api/node/mainThreadEditorsTracker.ts index 0c72506ad9f..f742f730926 100644 --- a/src/vs/workbench/api/node/mainThreadEditorsTracker.ts +++ b/src/vs/workbench/api/node/mainThreadEditorsTracker.ts @@ -579,7 +579,7 @@ export class MainThreadEditorsTracker { } private _findVisibleTextEditorIds(): string[] { - let result = []; + let result: string[] = []; let modelUris = Object.keys(this._model2TextEditors); for (let i = 0, len = modelUris.length; i < len; i++) { let editors = this._model2TextEditors[modelUris[i]]; diff --git a/src/vs/workbench/api/node/mainThreadTreeExplorers.ts b/src/vs/workbench/api/node/mainThreadTreeExplorers.ts index c0f8ded4be4..178c1e4ac93 100644 --- a/src/vs/workbench/api/node/mainThreadTreeExplorers.ts +++ b/src/vs/workbench/api/node/mainThreadTreeExplorers.ts @@ -16,7 +16,7 @@ export class MainThreadTreeExplorers extends MainThreadTreeExplorersShape { private _proxy: ExtHostTreeExplorersShape; constructor( - @IThreadService private threadService: IThreadService, + @IThreadService threadService: IThreadService, @ITreeExplorerService private treeExplorerService: ITreeExplorerService, @IMessageService private messageService: IMessageService, @ICommandService private commandService: ICommandService diff --git a/src/vs/workbench/api/node/mainThreadWorkspace.ts b/src/vs/workbench/api/node/mainThreadWorkspace.ts index cab823119aa..3a2223513a7 100644 --- a/src/vs/workbench/api/node/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/node/mainThreadWorkspace.ts @@ -30,9 +30,9 @@ export class MainThreadWorkspace extends MainThreadWorkspaceShape { constructor( @ISearchService searchService: ISearchService, @IWorkspaceContextService contextService: IWorkspaceContextService, - @ITextFileService textFileService, - @IWorkbenchEditorService editorService, - @ITextModelResolverService textModelResolverService, + @ITextFileService textFileService: ITextFileService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @ITextModelResolverService textModelResolverService: ITextModelResolverService, @IFileService fileService: IFileService ) { super(); diff --git a/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts b/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts index 7df18064577..ce92c4c1a14 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts @@ -22,7 +22,6 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi import { Position, POSITIONS } from 'vs/platform/editor/common/editor'; import { IEditorGroupService, ITabOptions, GroupArrangement, GroupOrientation } from 'vs/workbench/services/group/common/groupService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -139,7 +138,6 @@ export class EditorGroupsControl implements IEditorGroupsControl, IVerticalSashL @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @IEditorGroupService private editorGroupService: IEditorGroupService, @ITelemetryService private telemetryService: ITelemetryService, - @IConfigurationService private configurationService: IConfigurationService, @IContextKeyService private contextKeyService: IContextKeyService, @IExtensionService private extensionService: IExtensionService, @IInstantiationService private instantiationService: IInstantiationService, diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index cc3e6831b22..7ede1f4eb2b 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -425,7 +425,7 @@ export class TabsTitleControl extends TitleControl { } private hookTabListeners(tab: HTMLElement, index: number): IDisposable { - const disposables = []; + const disposables: IDisposable[] = []; // Open on Click disposables.push(DOM.addDisposableListener(tab, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => { diff --git a/src/vs/workbench/common/editor/editorStacksModel.ts b/src/vs/workbench/common/editor/editorStacksModel.ts index 9998b376d9a..94bad196395 100644 --- a/src/vs/workbench/common/editor/editorStacksModel.ts +++ b/src/vs/workbench/common/editor/editorStacksModel.ts @@ -559,7 +559,7 @@ export class EditorGroup implements IEditorGroup { // It is possible to have the same resource opened twice (once as normal input and once as diff input) // So we need to do ref counting on the resource to provide the correct picture let counter = this.mapResourceToEditorCount[resource.toString()] || 0; - let newCounter; + let newCounter: number; if (remove) { if (counter > 1) { newCounter = counter - 1; diff --git a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts index 3f866eb3759..4fd7b01fa9d 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts @@ -66,7 +66,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { } private getContextMenuActions(breakpoint: IBreakpoint, uri: uri, lineNumber: number): TPromise { - const actions = []; + const actions: IAction[] = []; if (breakpoint) { actions.push(this.instantiationService.createInstance(RemoveBreakpointAction, RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL)); actions.push(this.instantiationService.createInstance(EditConditionalBreakpointAction, EditConditionalBreakpointAction.ID, EditConditionalBreakpointAction.LABEL, this.editor, lineNumber)); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts index 6104389492a..344ef9cf51a 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts @@ -110,13 +110,13 @@ export class DebugHoverWidget implements IContentWidget { } private getExactExpressionRange(lineContent: string, range: Range): Range { - let matchingExpression = undefined; + let matchingExpression: string = undefined; let startOffset = 0; // Some example supported expressions: myVar.prop, a.b.c.d, myVar?.prop, myVar->prop, MyClass::StaticProp, *myVar // Match any character except a set of characters which often break interesting sub-expressions let expression: RegExp = /([^()\[\]{}<>\s+\-/%~#^;=|,`!]|\->)+/g; - let result = undefined; + let result: RegExpExecArray = undefined; // First find the full expression under the cursor while (result = expression.exec(lineContent)) { @@ -134,7 +134,7 @@ export class DebugHoverWidget implements IContentWidget { // For example in expression 'a.b.c.d', if the focus was under 'b', 'a.b' would be evaluated. if (matchingExpression) { let subExpression: RegExp = /\w+/g; - let subExpressionResult = undefined; + let subExpressionResult: RegExpExecArray = undefined; while (subExpressionResult = subExpression.exec(matchingExpression)) { let subEnd = subExpressionResult.index + 1 + startOffset + subExpressionResult[0].length; if (subEnd >= range.endColumn) { diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index 7b5e93d976f..64ecd31fb0b 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -29,7 +29,6 @@ import { MenuId } from 'vs/platform/actions/common/actions'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ReplExpressionsRenderer, ReplExpressionsController, ReplExpressionsDataSource, ReplExpressionsActionProvider, ReplExpressionsAccessibilityProvider } from 'vs/workbench/parts/debug/electron-browser/replViewer'; @@ -81,7 +80,6 @@ export class Repl extends Panel implements IPrivateReplService { constructor( @debug.IDebugService private debugService: debug.IDebugService, - @IContextMenuService private contextMenuService: IContextMenuService, @ITelemetryService telemetryService: ITelemetryService, @IInstantiationService private instantiationService: IInstantiationService, @IStorageService private storageService: IStorageService, diff --git a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts index 22b9551cb7c..ed9ad8d3cfd 100644 --- a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts @@ -373,7 +373,7 @@ export class ReplExpressionsRenderer implements IRenderer { pattern.lastIndex = 0; // the holy grail of software development const match = pattern.exec(text); - let resource = null; + let resource: uri = null; try { resource = match && uri.file(match[1]); } catch (e) { } diff --git a/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.ts b/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.ts index 183d1ddccd9..28ed164473a 100644 --- a/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.ts +++ b/src/vs/workbench/parts/explorers/browser/views/treeExplorerView.ts @@ -50,19 +50,11 @@ export class TreeExplorerView extends CollapsibleViewletView { const dataSource = this.instantiationService.createInstance(TreeDataSource, this.treeNodeProviderId); const renderer = this.instantiationService.createInstance(TreeRenderer, this.viewletState, this.actionRunner, container.getHTMLElement()); const controller = this.instantiationService.createInstance(TreeController, this.treeNodeProviderId); - const sorter = null; - const filter = null; - const dnd = null; - const accessibilityProvider = null; return new Tree(container.getHTMLElement(), { dataSource, renderer, - controller, - sorter, - filter, - dnd, - accessibilityProvider + controller }); } diff --git a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts index 524f44128e4..eba4ecb0047 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts @@ -313,7 +313,7 @@ export class DropDownMenuActionItem extends ActionItem { } private getActions(): IAction[] { - let actions = []; + let actions: IAction[] = []; const menuActionGroups = this.menuActionGroups.filter(group => group.some(action => action.enabled)); for (const menuActions of menuActionGroups) { actions = [...actions, ...menuActions, new Separator()]; diff --git a/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.ts b/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.ts index db0612bd1f8..7f99ba447e9 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsQuickOpen.ts @@ -72,7 +72,7 @@ export class GalleryExtensionsHandler extends QuickOpenHandler { } getResults(text: string): TPromise> { - const entries = []; + const entries: SimpleEntry[] = []; if (text) { const label = nls.localize('searchFor', "Press Enter to search for '{0}' in the Marketplace.", text); diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts index 9733f4c95de..94b082fb8ff 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -1174,7 +1174,6 @@ export class CompareResourcesAction extends Action { constructor( resource: URI, tree: ITree, - @IInstantiationService private instantiationService: IInstantiationService, @IWorkbenchEditorService private editorService: IWorkbenchEditorService ) { super('workbench.files.action.compareFiles', CompareResourcesAction.computeLabel()); diff --git a/src/vs/workbench/parts/files/browser/saveErrorHandler.ts b/src/vs/workbench/parts/files/browser/saveErrorHandler.ts index 8de0dfa8fc3..1e716709f59 100644 --- a/src/vs/workbench/parts/files/browser/saveErrorHandler.ts +++ b/src/vs/workbench/parts/files/browser/saveErrorHandler.ts @@ -183,7 +183,6 @@ class ResolveSaveConflictMessage implements IMessageWithAction { model: ITextFileEditorModel, message: string, @IMessageService private messageService: IMessageService, - @IInstantiationService private instantiationService: IInstantiationService, @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @IEnvironmentService private environmentService: IEnvironmentService ) { diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 197d0fb3dc5..c31f0532651 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -97,7 +97,7 @@ export class DefaultPreferencesEditor extends BaseTextEditor { public static ID: string = 'workbench.editor.defaultPreferences'; private static VIEW_STATE: Map = new Map(); - private inputDisposeListener; + private inputDisposeListener: IDisposable; private defaultSettingHeaderWidget: DefaultSettingsHeaderWidget; private delayedFilterLogging: Delayer; @@ -244,8 +244,8 @@ class DefaultPreferencesCodeEditor extends CodeEditor { } export interface IPreferencesRenderer { - render(); - dispose(); + render(): void; + dispose(): void; } export abstract class PreferencesEditorContribution extends Disposable implements editorCommon.IEditorContribution { diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index bca28da2a8b..9847b83b1f0 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -503,7 +503,7 @@ export class SearchModel extends Disposable { private currentRequest: PPromise; - constructor( @ISearchService private searchService, @ITelemetryService private telemetryService: ITelemetryService, @IInstantiationService private instantiationService: IInstantiationService) { + constructor( @ISearchService private searchService: ISearchService, @ITelemetryService private telemetryService: ITelemetryService, @IInstantiationService private instantiationService: IInstantiationService) { super(); this._searchResult = this.instantiationService.createInstance(SearchResult, this); } diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsTracker.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsTracker.ts index 1dce853f6a9..3d95492bca2 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsTracker.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsTracker.ts @@ -14,7 +14,6 @@ import { mkdirp, fileExists, readdir } from 'vs/base/node/pfs'; import { onUnexpectedError } from 'vs/base/common/errors'; import lifecycle = require('vs/base/common/lifecycle'); import { readAndRegisterSnippets } from 'vs/editor/node/textMate/TMSnippets'; -import { IFileService } from 'vs/platform/files/common/files'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { watch, FSWatcher } from 'fs'; @@ -28,7 +27,6 @@ export class SnippetsTracker implements workbenchExt.IWorkbenchContribution { private fileWatchDelayer: async.ThrottledDelayer; constructor( - @IFileService private fileService: IFileService, @ILifecycleService private lifecycleService: ILifecycleService, @IEnvironmentService environmentService: IEnvironmentService ) { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 6121cc1c58c..07181e043b5 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -306,7 +306,7 @@ export class TerminalInstance implements ITerminalInstance { } protected _getCwd(workspace: IWorkspace, ignoreCustomCwd: boolean): string { - let cwd; + let cwd: string; // TODO: Handle non-existent customCwd if (!ignoreCustomCwd) { @@ -355,7 +355,7 @@ export class TerminalInstance implements ITerminalInstance { this._onProcessIdReady.fire(this); } }); - this._process.on('exit', (exitCode) => { + this._process.on('exit', (exitCode: number) => { // Prevent dispose functions being triggered multiple times if (!this._isExiting) { this.dispose(); diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts index 40c3a86214e..85f19d8c247 100644 --- a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts +++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts @@ -16,10 +16,7 @@ import { IThemeService } from 'vs/workbench/services/themes/common/themeService' import { ReleaseNotesInput } from './releaseNotesInput'; import { EditorOptions } from 'vs/workbench/common/editor'; import WebView from 'vs/workbench/parts/html/browser/webview'; -import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IRequestService } from 'vs/platform/request/node/request'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IModeService } from 'vs/editor/common/services/modeService'; import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer'; @@ -46,10 +43,7 @@ export class ReleaseNotesEditor extends BaseEditor { constructor( @ITelemetryService telemetryService: ITelemetryService, @IThemeService private themeService: IThemeService, - @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @IRequestService private requestService: IRequestService, @IOpenerService private openerService: IOpenerService, - @IKeybindingService private keybindingService: IKeybindingService, @IModeService private modeService: IModeService ) { super(ReleaseNotesEditor.ID, telemetryService); diff --git a/src/vs/workbench/parts/update/electron-browser/update.ts b/src/vs/workbench/parts/update/electron-browser/update.ts index b2471615aee..c537d235a44 100644 --- a/src/vs/workbench/parts/update/electron-browser/update.ts +++ b/src/vs/workbench/parts/update/electron-browser/update.ts @@ -119,13 +119,12 @@ export class OpenLatestReleaseNotesInBrowserAction extends Action { export abstract class AbstractShowReleaseNotesAction extends Action { constructor( - id, - label, + id: string, + label: string, private returnValue: boolean, private version: string, @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @IInstantiationService private instantiationService: IInstantiationService, - @IOpenerService private openerService: IOpenerService + @IInstantiationService private instantiationService: IInstantiationService ) { super(id, label, null, true); } @@ -153,10 +152,9 @@ export class ShowReleaseNotesAction extends AbstractShowReleaseNotesAction { returnValue: boolean, version: string, @IWorkbenchEditorService editorService: IWorkbenchEditorService, - @IInstantiationService instantiationService: IInstantiationService, - @IOpenerService openerService: IOpenerService + @IInstantiationService instantiationService: IInstantiationService ) { - super('update.showReleaseNotes', nls.localize('releaseNotes', "Release Notes"), returnValue, version, editorService, instantiationService, openerService); + super('update.showReleaseNotes', nls.localize('releaseNotes', "Release Notes"), returnValue, version, editorService, instantiationService); } } @@ -169,10 +167,9 @@ export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesActio id = ShowCurrentReleaseNotesAction.ID, label = ShowCurrentReleaseNotesAction.LABEL, @IWorkbenchEditorService editorService: IWorkbenchEditorService, - @IInstantiationService instantiationService: IInstantiationService, - @IOpenerService openerService: IOpenerService + @IInstantiationService instantiationService: IInstantiationService ) { - super(id, label, true, pkg.version, editorService, instantiationService, openerService); + super(id, label, true, pkg.version, editorService, instantiationService); } } diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index f028e5e8491..b1ef6c8fa37 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -187,7 +187,7 @@ export class WorkspaceConfigurationService implements IWorkspaceConfigurationSer this.cachedWorkspaceConfig = workspaceConfig; // Cache keys - const workspaceConfigKeys = []; + const workspaceConfigKeys: string[] = []; Object.keys(workspaceConfigFiles).forEach(path => { if (path === WORKSPACE_CONFIG_DEFAULT_PATH) { workspaceConfigKeys.push(...Object.keys(workspaceConfigFiles[path].raw)); diff --git a/src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts index aa4fd21fe51..193e17db95b 100644 --- a/src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts @@ -25,7 +25,6 @@ import URI from 'vs/base/common/uri'; import { FileService } from 'vs/workbench/services/files/node/fileService'; import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService'; import { ConfigurationTarget, IConfigurationEditingError, ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IFileService } from 'vs/platform/files/common/files'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -56,7 +55,6 @@ class TestDirtyTextFileService extends TestTextFileService { @IConfigurationService configurationService: IConfigurationService, @ITelemetryService telemetryService: ITelemetryService, @IWorkbenchEditorService editorService: IWorkbenchEditorService, - @IEditorGroupService editorGroupService: IEditorGroupService, @IFileService fileService: IFileService, @IUntitledEditorService untitledEditorService: IUntitledEditorService, @IInstantiationService instantiationService: IInstantiationService, @@ -64,7 +62,7 @@ class TestDirtyTextFileService extends TestTextFileService { @IBackupFileService backupFileService: IBackupFileService, @IWindowsService windowsService: IWindowsService ) { - super(lifecycleService, contextService, configurationService, telemetryService, editorService, editorGroupService, fileService, untitledEditorService, instantiationService, messageService, backupFileService, windowsService); + super(lifecycleService, contextService, configurationService, telemetryService, editorService, fileService, untitledEditorService, instantiationService, messageService, backupFileService, windowsService); } public isDirty(resource?: URI): boolean { diff --git a/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts index f214ca62645..71a230c677c 100644 --- a/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts @@ -221,7 +221,7 @@ export class ConfigurationResolverService implements IConfigurationResolverServi const factory: { (): TPromise }[] = Object.keys(interactiveVariablesToSubstitutes).map(interactiveVariable => { return () => { - let commandId = null; + let commandId: string = null; commandId = interactiveVariablesMap ? interactiveVariablesMap[interactiveVariable] : null; if (!commandId) { // Just launch any command if the interactive variable is not contributed by the adapter #12735 diff --git a/src/vs/workbench/services/search/node/textSearch.ts b/src/vs/workbench/services/search/node/textSearch.ts index 65ef0a1121b..eb82da7bf10 100644 --- a/src/vs/workbench/services/search/node/textSearch.ts +++ b/src/vs/workbench/services/search/node/textSearch.ts @@ -121,7 +121,7 @@ export class Engine implements ISearchEngine { }; // Walk over the file system - let nextBatch = []; + let nextBatch: string[] = []; let nextBatchBytes = 0; const batchFlushBytes = 2 ** 20; // 1MB this.walker.walk(this.config.rootFolders, this.config.extraFiles, result => { diff --git a/src/vs/workbench/services/search/node/worker/searchWorker.ts b/src/vs/workbench/services/search/node/worker/searchWorker.ts index 2f5c52809fa..4f845374d15 100644 --- a/src/vs/workbench/services/search/node/worker/searchWorker.ts +++ b/src/vs/workbench/services/search/node/worker/searchWorker.ts @@ -177,7 +177,7 @@ export class SearchWorkerEngine { let lineNumber = 0; let lastBufferHadTraillingCR = false; - const decodeBuffer = (buffer: NodeBuffer, start, end): string => { + const decodeBuffer = (buffer: NodeBuffer, start: number, end: number): string => { if (options.encoding === UTF8 || options.encoding === UTF8_with_bom) { return buffer.toString(undefined, start, end); // much faster to use built in toString() when encoding is default } diff --git a/src/vs/workbench/services/telemetry/common/workspaceStats.ts b/src/vs/workbench/services/telemetry/common/workspaceStats.ts index 79ad84e73e9..7f64418ade1 100644 --- a/src/vs/workbench/services/telemetry/common/workspaceStats.ts +++ b/src/vs/workbench/services/telemetry/common/workspaceStats.ts @@ -60,7 +60,8 @@ function extractDomain(url: string): string { } export function getDomainsOfRemotes(text: string, whitelist: string[]): string[] { - let domains = new ArraySet(), match; + let domains = new ArraySet(); + let match: RegExpExecArray; while (match = RemoteMatcher.exec(text)) { let domain = extractDomain(match[1]); if (domain) { @@ -242,7 +243,7 @@ export class WorkspaceStats { ); } - private reportAzure(uri) { + private reportAzure(uri: URI) { const tags: Tags = Object.create(null); this.reportAzureNode(uri, tags).then((tags) => { return this.reportAzureJava(uri, tags); diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index c65c4a8fcc1..9da33446228 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -23,7 +23,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; @@ -61,7 +60,6 @@ export abstract class TextFileService implements ITextFileService { @IWorkspaceContextService private contextService: IWorkspaceContextService, @IConfigurationService private configurationService: IConfigurationService, @ITelemetryService private telemetryService: ITelemetryService, - @IEditorGroupService private editorGroupService: IEditorGroupService, @IFileService protected fileService: IFileService, @IUntitledEditorService private untitledEditorService: IUntitledEditorService, @IInstantiationService private instantiationService: IInstantiationService, diff --git a/src/vs/workbench/services/textfile/electron-browser/textFileService.ts b/src/vs/workbench/services/textfile/electron-browser/textFileService.ts index 9455e4c12d7..31689440954 100644 --- a/src/vs/workbench/services/textfile/electron-browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/textFileService.ts @@ -22,7 +22,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService'; -import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModelBuilder } from 'vs/editor/node/model/modelBuilder'; import product from 'vs/platform/node/product'; @@ -46,7 +45,6 @@ export class TextFileService extends AbstractTextFileService { @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService configurationService: IConfigurationService, @IModeService private modeService: IModeService, - @IEditorGroupService editorGroupService: IEditorGroupService, @IWindowIPCService private windowService: IWindowIPCService, @IModelService private modelService: IModelService, @IEnvironmentService environmentService: IEnvironmentService, @@ -55,7 +53,7 @@ export class TextFileService extends AbstractTextFileService { @IStorageService private storageService: IStorageService, @IWindowsService windowsService: IWindowsService ) { - super(lifecycleService, contextService, configurationService, telemetryService, editorGroupService, fileService, untitledEditorService, instantiationService, messageService, environmentService, backupFileService, windowsService); + super(lifecycleService, contextService, configurationService, telemetryService, fileService, untitledEditorService, instantiationService, messageService, environmentService, backupFileService, windowsService); } public resolveTextContent(resource: URI, options?: IResolveContentOptions): TPromise { diff --git a/src/vs/workbench/services/themes/electron-browser/stylesContributions.ts b/src/vs/workbench/services/themes/electron-browser/stylesContributions.ts index 27832128a7a..e6ed64d35cc 100644 --- a/src/vs/workbench/services/themes/electron-browser/stylesContributions.ts +++ b/src/vs/workbench/services/themes/electron-browser/stylesContributions.ts @@ -94,7 +94,7 @@ export class TokenStylesContribution { public contributeStyles(themeId: string, themeDocument: IThemeDocument, cssRules: string[]): void { let theme = new Theme(themeId, themeDocument); - theme.getSettings().forEach((s: IThemeSetting, index, arr) => { + theme.getSettings().forEach((s: IThemeSetting, index: number, arr: IThemeSetting[]) => { // @martin TS(2.0.2) - s.scope is already a string[] so no need for all this checking. // However will add a cast at split to keep semantic in case s.scope is wrongly typed. let scope: string | string[] = s.scope; @@ -127,7 +127,7 @@ export class TokenStylesContribution { //statements.push(`background-color: ${background};`); break; case 'fontStyle': - let segments = value.split(' '); + let segments: string[] = value.split(' '); segments.forEach(s => { switch (s) { case 'italic': diff --git a/src/vs/workbench/services/themes/electron-browser/themeService.ts b/src/vs/workbench/services/themes/electron-browser/themeService.ts index fc42eb47f2d..521268516b3 100644 --- a/src/vs/workbench/services/themes/electron-browser/themeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/themeService.ts @@ -434,7 +434,7 @@ export class ThemeService implements IThemeService { private _updateIconTheme(onApply: (theme: IInternalThemeData) => void): TPromise { return this.getFileIconThemes().then(allIconSets => { - let iconSetData; + let iconSetData: IInternalThemeData; for (let iconSet of allIconSets) { if (iconSet.id === this.currentIconTheme) { iconSetData = iconSet; @@ -533,7 +533,7 @@ function _processIconThemeDocument(id: string, iconThemeDocumentPath: string, ic let fileExtensions = associations.fileExtensions; if (fileExtensions) { for (let fileExtension in fileExtensions) { - let selectors = []; + let selectors: string[] = []; let segments = fileExtension.toLowerCase().split('.'); for (let i = 0; i < segments.length; i++) { selectors.push(`.${escapeCSS(segments.slice(i).join('.'))}-ext-file-icon`); @@ -544,7 +544,7 @@ function _processIconThemeDocument(id: string, iconThemeDocumentPath: string, ic let fileNames = associations.fileNames; if (fileNames) { for (let fileName in fileNames) { - let selectors = []; + let selectors: string[] = []; let segments = fileName.toLowerCase().split('.'); if (segments[0]) { selectors.push(`.${escapeCSS(segments[0])}-name-file-icon`); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index b21fbb11261..b3cb1743a4b 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -110,7 +110,6 @@ export class TestTextFileService extends TextFileService { @IConfigurationService configurationService: IConfigurationService, @ITelemetryService telemetryService: ITelemetryService, @IWorkbenchEditorService editorService: IWorkbenchEditorService, - @IEditorGroupService editorGroupService: IEditorGroupService, @IFileService fileService: IFileService, @IUntitledEditorService untitledEditorService: IUntitledEditorService, @IInstantiationService instantiationService: IInstantiationService, @@ -118,7 +117,7 @@ export class TestTextFileService extends TextFileService { @IBackupFileService backupFileService: IBackupFileService, @IWindowsService windowsService: IWindowsService ) { - super(lifecycleService, contextService, configurationService, telemetryService, editorGroupService, fileService, untitledEditorService, instantiationService, messageService, TestEnvironmentService, backupFileService, windowsService); + super(lifecycleService, contextService, configurationService, telemetryService, fileService, untitledEditorService, instantiationService, messageService, TestEnvironmentService, backupFileService, windowsService); } public setPromptPath(path: string): void { From ccb76ec07e7c76b8a86a5a02a2692421cec6a54c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 31 Dec 2016 09:32:07 +0100 Subject: [PATCH 029/131] support forceReuseWindow in openWindow API --- src/vs/platform/windows/common/windows.ts | 4 ++-- src/vs/platform/windows/common/windowsIpc.ts | 8 ++++---- src/vs/platform/windows/electron-main/windowsService.ts | 4 ++-- src/vs/workbench/electron-browser/actions.ts | 4 ++-- src/vs/workbench/electron-browser/window.ts | 2 +- .../parts/files/electron-browser/electronFileActions.ts | 2 +- .../files/electron-browser/files.electron.contribution.ts | 2 +- src/vs/workbench/parts/git/electron-browser/gitActions.ts | 2 +- .../snippets/electron-browser/snippets.contribution.ts | 2 +- src/vs/workbench/test/workbenchTestServices.ts | 3 +-- 10 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 000fd229a12..6fc7c717bd9 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -25,6 +25,7 @@ export interface IWindowsService { openDevTools(windowId: number): TPromise; toggleDevTools(windowId: number): TPromise; // TODO@joao: rename, shouldn't this be closeWindow? + // @ben: no, this actually leaves the window open but changes it to have no workspace opened closeFolder(windowId: number): TPromise; toggleFullScreen(windowId: number): TPromise; setRepresentedFilename(windowId: number, fileName: string): TPromise; @@ -40,8 +41,7 @@ export interface IWindowsService { quit(): TPromise; // Global methods - // TODO@joao: rename, shouldn't this be openWindow? - windowOpen(paths: string[], forceNewWindow?: boolean): TPromise; + openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean }): TPromise; openNewWindow(): TPromise; showWindow(windowId: number): TPromise; getWindows(): TPromise<{ id: number; path: string; title: string; }[]>; diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index 42369382626..a02689a28ef 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -31,7 +31,7 @@ export interface IWindowsChannel extends IChannel { call(command: 'setDocumentEdited', arg: [number, boolean]): TPromise; call(command: 'toggleMenuBar', arg: number): TPromise; call(command: 'quit'): TPromise; - call(command: 'windowOpen', arg: [string[], boolean]): TPromise; + call(command: 'openWindow', arg: [string[], { forceNewWindow?: boolean, forceReuseWindow?: boolean }]): TPromise; call(command: 'openNewWindow'): TPromise; call(command: 'showWindow', arg: number): TPromise; call(command: 'getWindows'): TPromise<{ id: number; path: string; title: string; }[]>; @@ -76,7 +76,7 @@ export class WindowsChannel implements IWindowsChannel { case 'unmaximizeWindow': return this.service.unmaximizeWindow(arg); case 'setDocumentEdited': return this.service.setDocumentEdited(arg[0], arg[1]); case 'toggleMenuBar': return this.service.toggleMenuBar(arg); - case 'windowOpen': return this.service.windowOpen(arg[0], arg[1]); + case 'openWindow': return this.service.openWindow(arg[0], arg[1]); case 'openNewWindow': return this.service.openNewWindow(); case 'showWindow': return this.service.showWindow(arg); case 'getWindows': return this.service.getWindows(); @@ -179,8 +179,8 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('quit'); } - windowOpen(paths: string[], forceNewWindow?: boolean): TPromise { - return this.channel.call('windowOpen', [paths, forceNewWindow]); + openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean }): TPromise { + return this.channel.call('openWindow', [paths, options]); } openNewWindow(): TPromise { diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index aa9adf73444..7b0e6d47e4b 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -198,12 +198,12 @@ export class WindowsService implements IWindowsService, IDisposable { return TPromise.as(null); } - windowOpen(paths: string[], forceNewWindow?: boolean): TPromise { + openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean }): TPromise { if (!paths || !paths.length) { return TPromise.as(null); } - this.windowsMainService.open({ context: OpenContext.OTHER, cli: this.environmentService.args, pathsToOpen: paths, forceNewWindow }); + this.windowsMainService.open({ context: OpenContext.OTHER, cli: this.environmentService.args, pathsToOpen: paths, forceNewWindow: options && options.forceNewWindow, forceReuseWindow: options && options.forceReuseWindow }); return TPromise.as(null); } diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index a29af01a04e..10f15341b35 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -588,8 +588,8 @@ export class OpenRecentAction extends Action { } const runPick = (path: string, context: IEntryRunContext) => { - const newWindow = context.keymods.indexOf(KeyMod.CtrlCmd) >= 0; - this.windowsService.windowOpen([path], newWindow); + const forceNewWindow = context.keymods.indexOf(KeyMod.CtrlCmd) >= 0; + this.windowsService.openWindow([path], { forceNewWindow }); }; const folderPicks: IFilePickOpenEntry[] = recentFolders.map((p, index) => toPick(p, index === 0 ? { label: nls.localize('folders', "folders") } : void 0, true)); diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 012c423cb3a..1f1d712bb9d 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -137,7 +137,7 @@ export class ElectronWindow { DOM.EventHelper.stop(e, true); this.focus(); // make sure this window has focus so that the open call reaches the right window! - this.windowsService.windowOpen(draggedExternalResources.map(r => r.fsPath)); + this.windowsService.openWindow(draggedExternalResources.map(r => r.fsPath), { forceReuseWindow: true }); cleanUp(); }) diff --git a/src/vs/workbench/parts/files/electron-browser/electronFileActions.ts b/src/vs/workbench/parts/files/electron-browser/electronFileActions.ts index 53014f290b0..a22a107ba2b 100644 --- a/src/vs/workbench/parts/files/electron-browser/electronFileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/electronFileActions.ts @@ -154,7 +154,7 @@ export class ShowOpenedFileInNewWindow extends Action { public run(): TPromise { const fileResource = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' }); if (fileResource) { - this.windowsService.windowOpen([fileResource.fsPath], true); + this.windowsService.openWindow([fileResource.fsPath], { forceNewWindow: true }); } else { this.messageService.show(severity.Info, nls.localize('openFileToShow', "Open a file first to open in new window")); } diff --git a/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts b/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts index bf7b04b24f5..93bfd9df732 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.electron.contribution.ts @@ -92,7 +92,7 @@ CommandsRegistry.registerCommand('_files.openFolderPicker', (accessor: ServicesA CommandsRegistry.registerCommand('_files.windowOpen', (accessor: ServicesAccessor, paths: string[], forceNewWindow: boolean) => { const windowsService = accessor.get(IWindowsService); - windowsService.windowOpen(paths, forceNewWindow); + windowsService.openWindow(paths, { forceNewWindow }); }); CommandsRegistry.registerCommand('workbench.action.files.openFileInNewWindow', (accessor: ServicesAccessor) => { diff --git a/src/vs/workbench/parts/git/electron-browser/gitActions.ts b/src/vs/workbench/parts/git/electron-browser/gitActions.ts index f983b03b2c3..1a809c3fbab 100644 --- a/src/vs/workbench/parts/git/electron-browser/gitActions.ts +++ b/src/vs/workbench/parts/git/electron-browser/gitActions.ts @@ -70,7 +70,7 @@ export class CloneAction extends Action { return clone.then(path => { const forceNewWindow = this.workspaceService.hasWorkspace(); - return this.windowsService.windowOpen([path], forceNewWindow); + return this.windowsService.openWindow([path], { forceNewWindow, forceReuseWindow: !forceNewWindow }); }).then(null, e => { if (/already exists and is not an empty directory/.test(e.stderr || '')) { diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts b/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts index ef9c163a4ae..9c05c47a36c 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts @@ -40,7 +40,7 @@ class OpenSnippetsAction extends actions.Action { } private openFile(filePath: string): winjs.TPromise { - return this.windowsService.windowOpen([filePath]); + return this.windowsService.openWindow([filePath], { forceReuseWindow: true }); } public run(): winjs.Promise { diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index b3cb1743a4b..4321cd6487b 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -887,8 +887,7 @@ export class TestWindowsService implements IWindowsService { } // Global methods - // TODO@joao: rename, shouldn't this be openWindow? - windowOpen(paths: string[], forceNewWindow?: boolean): TPromise { + openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean }): TPromise { return TPromise.as(void 0); } openNewWindow(): TPromise { From 3955048967790636bced948caf0f08ec8402a10e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 31 Dec 2016 09:48:45 +0100 Subject: [PATCH 030/131] :liptstick: --- src/vs/code/electron-main/windows.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 3ba7ff6e107..6c088b61ca5 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -207,7 +207,12 @@ export class WindowsManager implements IWindowsMainService { // Handle paths delayed in case more are coming! runningTimeout = setTimeout(() => { - this.open({ context: OpenContext.DOCK, cli: this.environmentService.args, pathsToOpen: macOpenFiles, preferNewWindow: true /* dropping on the dock prefers to open in a new window */ }); + this.open({ + context: OpenContext.DOCK /* can also be opening from finder while app is running */, + cli: this.environmentService.args, + pathsToOpen: macOpenFiles, + preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */ + }); macOpenFiles = []; runningTimeout = null; }, 100); @@ -381,7 +386,7 @@ export class WindowsManager implements IWindowsMainService { if (openConfig.forceNewWindow || openConfig.forceReuseWindow) { openFilesInNewWindow = openConfig.forceNewWindow && !openConfig.forceReuseWindow; } else { - if (openConfig.preferNewWindow && openConfig.context === OpenContext.DOCK) { + if (openConfig.context === OpenContext.DOCK) { openFilesInNewWindow = true; // only on macOS do we allow to open files in a new window if this is triggered via DOCK context } From 31042a9186909fdd5e4dd83b7dcbb6d8bc053921 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 31 Dec 2016 10:18:19 +0100 Subject: [PATCH 031/131] turn openFoldersInNewWindow and openFilesInNewWindow into an enum and explain behaviour --- src/vs/code/electron-main/windows.ts | 8 ++--- src/vs/platform/windows/common/windows.ts | 4 +-- .../electron-browser/main.contribution.ts | 34 +++++++++++++++---- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 6c088b61ca5..05775290bf3 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -374,8 +374,8 @@ export class WindowsManager implements IWindowsMainService { // let the user settings override how folders are open in a new window or same window unless we are forced let openFolderInNewWindow = (openConfig.preferNewWindow || openConfig.forceNewWindow) && !openConfig.forceReuseWindow; - if (!openConfig.forceNewWindow && !openConfig.forceReuseWindow && windowConfig && typeof windowConfig.openFoldersInNewWindow === 'boolean') { - openFolderInNewWindow = windowConfig.openFoldersInNewWindow; + if (!openConfig.forceNewWindow && !openConfig.forceReuseWindow && windowConfig && (windowConfig.openFoldersInNewWindow === 'on' || windowConfig.openFoldersInNewWindow === 'off')) { + openFolderInNewWindow = (windowConfig.openFoldersInNewWindow === 'on'); } // Handle files to open/diff or to create when we dont open a folder @@ -390,8 +390,8 @@ export class WindowsManager implements IWindowsMainService { openFilesInNewWindow = true; // only on macOS do we allow to open files in a new window if this is triggered via DOCK context } - if (!openConfig.cli.extensionDevelopmentPath && windowConfig && typeof windowConfig.openFilesInNewWindow === 'boolean') { - openFilesInNewWindow = windowConfig.openFilesInNewWindow; + if (!openConfig.cli.extensionDevelopmentPath && windowConfig && (windowConfig.openFilesInNewWindow === 'on' || windowConfig.openFilesInNewWindow === 'off')) { + openFilesInNewWindow = (windowConfig.openFilesInNewWindow === 'on'); } } diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 6fc7c717bd9..51aee630f63 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -87,8 +87,8 @@ export interface IWindowService { } export interface IWindowSettings { - openFilesInNewWindow: boolean; - openFoldersInNewWindow: boolean; + openFilesInNewWindow: 'on' | 'off' | 'default'; + openFoldersInNewWindow: 'on' | 'off' | 'default'; reopenFolders: 'all' | 'one' | 'none'; restoreFullscreen: boolean; zoomLevel: number; diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index ad735935c75..f3b8f38466e 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -171,14 +171,36 @@ configurationRegistry.registerConfiguration({ // Configuration: Window let properties: { [path: string]: IJSONSchema; } = { 'window.openFilesInNewWindow': { - 'type': 'boolean', - 'default': false, - 'description': nls.localize('openFilesInNewWindow', "When enabled, will prefer to open files in a new window instead of reusing an existing instance. Note that there can still be cases where this setting is ignored (e.g. when using the -n command line option).") + 'type': 'string', + 'enum': ['on', 'off', 'default'], + 'default': 'default', + 'description': platform.isMacintosh ? + nls.localize('openFilesInNewWindowMac', + `Controls if files should open in a new window or the last active window. +- default: files will open in the last active window unless opened via the dock or from finder +- on: files will open in a new window +- off: files will open in the last active window +Note that there can still be cases where this setting is ignored (e.g. when using the -new-window or -reuse-window command line option).` + ) : + nls.localize('openFilesInNewWindow', + `Controls if files should open in a new window or the last active window. +- default: files will open in the last active window +- on: files will open in a new window +- off: files will open in the last active window +Note that there can still be cases where this setting is ignored (e.g. when using the -new-window or -reuse-window command line option).` + ) }, 'window.openFoldersInNewWindow': { - 'type': 'boolean', - 'default': false, - 'description': nls.localize('openFoldersInNewWindow', "When enabled, will prefer to open folders in a new window instead of reusing an existing instance. Note that there can still be cases where this setting is ignored (e.g. when using the -n command line option).") + 'type': 'string', + 'enum': ['on', 'off', 'default'], + 'default': 'default', + 'description': nls.localize('openFoldersInNewWindow', + `Controls if folders should open in a new window or the last active window. +- default: folders will open in a new window unless a folder is picked from within the application (e.g. via the File menu) +- on: folders will open in a new window +- off: folders will open in the last active window +Note that there can still be cases where this setting is ignored (e.g. when using the -new-window or -reuse-window command line option).` + ) }, 'window.reopenFolders': { 'type': 'string', From 81e35f4732219a9f1575d13977389a8be8447206 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 31 Dec 2016 11:05:15 +0100 Subject: [PATCH 032/131] still support openFilesInNewWindow: false for backwards compatibility --- src/vs/code/electron-main/windows.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 05775290bf3..7c442c543d9 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -390,7 +390,7 @@ export class WindowsManager implements IWindowsMainService { openFilesInNewWindow = true; // only on macOS do we allow to open files in a new window if this is triggered via DOCK context } - if (!openConfig.cli.extensionDevelopmentPath && windowConfig && (windowConfig.openFilesInNewWindow === 'on' || windowConfig.openFilesInNewWindow === 'off')) { + if (!openConfig.cli.extensionDevelopmentPath && windowConfig && (windowConfig.openFilesInNewWindow === 'on' || windowConfig.openFilesInNewWindow === 'off' || windowConfig.openFilesInNewWindow === false /* TODO@Ben migration */)) { openFilesInNewWindow = (windowConfig.openFilesInNewWindow === 'on'); } } From d384271ffb5655cee1ddf8f93f51cd892ee2cc40 Mon Sep 17 00:00:00 2001 From: roblou Date: Mon, 2 Jan 2017 11:49:57 -0800 Subject: [PATCH 033/131] node-debug2@1.9.1 --- build/gulpfile.vscode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 3130c7dd97e..e0359c5d0eb 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -40,7 +40,7 @@ const nodeModules = ['electron', 'original-fs'] const builtInExtensions = [ { name: 'ms-vscode.node-debug', version: '1.9.0' }, - { name: 'ms-vscode.node-debug2', version: '1.9.0' } + { name: 'ms-vscode.node-debug2', version: '1.9.1' } ]; const vscodeEntryPoints = _.flatten([ From 33e799b85ebd77789dedbab52ae5b212326778c4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 3 Jan 2017 07:45:35 +0100 Subject: [PATCH 034/131] RenameController depends on UNKNOWN service fileService (fixes #18002) --- src/vs/editor/common/services/bulkEdit.ts | 31 ++++++++++--------- .../editor/contrib/rename/browser/rename.ts | 7 +++-- .../workbench/api/node/mainThreadWorkspace.ts | 2 +- .../parts/search/browser/replaceService.ts | 2 +- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/vs/editor/common/services/bulkEdit.ts b/src/vs/editor/common/services/bulkEdit.ts index 90c3ae98474..bb877af77af 100644 --- a/src/vs/editor/common/services/bulkEdit.ts +++ b/src/vs/editor/common/services/bulkEdit.ts @@ -34,7 +34,7 @@ class ChangeRecorder { private _fileService: IFileService; - constructor(fileService: IFileService) { + constructor(fileService?: IFileService) { this._fileService = fileService; } @@ -42,22 +42,25 @@ class ChangeRecorder { const changes: IStringDictionary = Object.create(null); - const stop = this._fileService.onFileChanges((event) => { - event.changes.forEach(change => { + let stop: IDisposable; + if (this._fileService) { + stop = this._fileService.onFileChanges((event) => { + event.changes.forEach(change => { - const key = String(change.resource); - let array = changes[key]; + const key = String(change.resource); + let array = changes[key]; - if (!array) { - changes[key] = array = []; - } + if (!array) { + changes[key] = array = []; + } - array.push(change); + array.push(change); + }); }); - }); + } return { - stop: () => { stop.dispose(); }, + stop: () => { return stop && stop.dispose(); }, hasChanged: (resource: URI) => !!changes[resource.toString()], allChanges: () => flatten(values(changes)) }; @@ -273,14 +276,14 @@ export interface BulkEdit { finish(): TPromise; } -export function bulkEdit(fileService: IFileService, textModelResolverService: ITextModelResolverService, editor: ICommonCodeEditor, edits: IResourceEdit[], progress: IProgressRunner = null): TPromise { - let bulk = createBulkEdit(fileService, textModelResolverService, editor); +export function bulkEdit(textModelResolverService: ITextModelResolverService, editor: ICommonCodeEditor, edits: IResourceEdit[], fileService?: IFileService, progress: IProgressRunner = null): TPromise { + let bulk = createBulkEdit(textModelResolverService, editor, fileService); bulk.add(edits); bulk.progress(progress); return bulk.finish(); } -export function createBulkEdit(fileService: IFileService, textModelResolverService: ITextModelResolverService, editor: ICommonCodeEditor): BulkEdit { +export function createBulkEdit(textModelResolverService: ITextModelResolverService, editor?: ICommonCodeEditor, fileService?: IFileService): BulkEdit { let all: IResourceEdit[] = []; let recording = new ChangeRecorder(fileService).start(); diff --git a/src/vs/editor/contrib/rename/browser/rename.ts b/src/vs/editor/contrib/rename/browser/rename.ts index 241bf5bd349..249486f2b8f 100644 --- a/src/vs/editor/contrib/rename/browser/rename.ts +++ b/src/vs/editor/contrib/rename/browser/rename.ts @@ -22,6 +22,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { rename } from '../common/rename'; import RenameInputField from './renameInputField'; import { ITextModelResolverService } from 'vs/editor/common/services/resolverService'; +import { optional } from 'vs/platform/instantiation/common/instantiation'; // --- register actions and commands @@ -42,10 +43,10 @@ class RenameController implements IEditorContribution { constructor( private editor: ICodeEditor, @IMessageService private _messageService: IMessageService, - @IFileService private _fileService: IFileService, @ITextModelResolverService private _textModelResolverService: ITextModelResolverService, @IProgressService private _progressService: IProgressService, - @IContextKeyService contextKeyService: IContextKeyService + @IContextKeyService contextKeyService: IContextKeyService, + @optional(IFileService) private _fileService: IFileService ) { this._renameInputField = new RenameInputField(editor); this._renameInputVisible = CONTEXT_RENAME_INPUT_VISIBLE.bindTo(contextKeyService); @@ -132,7 +133,7 @@ class RenameController implements IEditorContribution { // start recording of file changes so that we can figure out if a file that // is to be renamed conflicts with another (concurrent) modification - let edit = createBulkEdit(this._fileService, this._textModelResolverService, this.editor); + let edit = createBulkEdit(this._textModelResolverService, this.editor, this._fileService); return rename(this.editor.getModel(), this.editor.getPosition(), newName).then(result => { if (result.rejectReason) { diff --git a/src/vs/workbench/api/node/mainThreadWorkspace.ts b/src/vs/workbench/api/node/mainThreadWorkspace.ts index 3a2223513a7..6acf4ad942b 100644 --- a/src/vs/workbench/api/node/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/node/mainThreadWorkspace.ts @@ -99,7 +99,7 @@ export class MainThreadWorkspace extends MainThreadWorkspaceShape { } } - return bulkEdit(this._fileService, this._textModelResolverService, codeEditor, edits) + return bulkEdit(this._textModelResolverService, codeEditor, edits, this._fileService) .then(() => true); } } diff --git a/src/vs/workbench/parts/search/browser/replaceService.ts b/src/vs/workbench/parts/search/browser/replaceService.ts index c64faa6cde3..f396e9f023d 100644 --- a/src/vs/workbench/parts/search/browser/replaceService.ts +++ b/src/vs/workbench/parts/search/browser/replaceService.ts @@ -97,7 +97,7 @@ export class ReplaceService implements IReplaceService { public replace(match: FileMatchOrMatch, progress?: IProgressRunner, resource?: URI): TPromise public replace(arg: any, progress: IProgressRunner = null, resource: URI = null): TPromise { - let bulkEdit: BulkEdit = createBulkEdit(this.fileService, this.textModelResolverService, null); + let bulkEdit: BulkEdit = createBulkEdit(this.textModelResolverService, null, this.fileService); bulkEdit.progress(progress); if (arg instanceof Match) { From 21819ab9edf1fc7f24a8dfd4dccee532c827c438 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 3 Jan 2017 09:48:23 +0100 Subject: [PATCH 035/131] Revert "pass on args when invoking an action" This reverts commit bc46e789ac57f327f409260fb8cc1f6e40e62a7b. --- src/vs/workbench/common/actionRegistry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/common/actionRegistry.ts b/src/vs/workbench/common/actionRegistry.ts index 1e904a5ab78..fa3b8578ea4 100644 --- a/src/vs/workbench/common/actionRegistry.ts +++ b/src/vs/workbench/common/actionRegistry.ts @@ -175,7 +175,7 @@ export function triggerAndDisposeAction(instantitationService: IInstantiationSer // run action when workbench is created return partService.joinCreation().then(() => { try { - return TPromise.as(actionInstance.run(args)).then(() => { + return TPromise.as(actionInstance.run()).then(() => { actionInstance.dispose(); }, (err) => { actionInstance.dispose(); From e538971529c58373f7e3d6056aaa82f25bbfc5c2 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 3 Jan 2017 11:04:06 +0100 Subject: [PATCH 036/131] undo bad changes, fixes #17989 --- .../editor/contrib/snippet/common/snippet.ts | 18 +------- .../snippet/test/common/snippet.test.ts | 46 ++++++++++++++----- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/vs/editor/contrib/snippet/common/snippet.ts b/src/vs/editor/contrib/snippet/common/snippet.ts index 0c9f9766732..6392444d8f3 100644 --- a/src/vs/editor/contrib/snippet/common/snippet.ts +++ b/src/vs/editor/contrib/snippet/common/snippet.ts @@ -122,25 +122,11 @@ export class CodeSnippet implements ICodeSnippet { for (let {startLineNumber, startColumn, endLineNumber, endColumn} of originalPlaceHolder.occurences) { - if (startColumn > 1) { - // placeholders that aren't at the beginning of the snippet line - // will be moved by how many characters the indentation has been - // adjusted - startColumn = startColumn + deltaColumns[startLineNumber]; - endColumn = endColumn + deltaColumns[endLineNumber]; - - } else { - // placeholders at the beginning of the snippet line - // will be indented by the reference indentation - startColumn += referenceIndentation.length; - endColumn += referenceIndentation.length; - } - resultOccurences.push({ startLineNumber: startLineNumber + deltaLine, - startColumn, + startColumn: startColumn + deltaColumns[startLineNumber], endLineNumber: endLineNumber + deltaLine, - endColumn + endColumn: endColumn + deltaColumns[endLineNumber] }); } diff --git a/src/vs/editor/contrib/snippet/test/common/snippet.test.ts b/src/vs/editor/contrib/snippet/test/common/snippet.test.ts index 63a302d66f3..37177a34e87 100644 --- a/src/vs/editor/contrib/snippet/test/common/snippet.test.ts +++ b/src/vs/editor/contrib/snippet/test/common/snippet.test.ts @@ -225,28 +225,50 @@ suite('Editor Contrib - Snippets', () => { }); - test('issue #11890: Bad cursor position', () => { + // test('issue #11890: Bad cursor position', () => { - let snippet = CodeSnippet.fromTextmate([ - 'afterEach((done) => {', - '${1}\ttest${2}', - '})' - ].join('\n')); + // let snippet = CodeSnippet.fromTextmate([ + // 'afterEach((done) => {', + // '${1}\ttest${2}', + // '})' + // ].join('\n')); - let boundSnippet = snippet.bind('', 0, 0, { + // let boundSnippet = snippet.bind('', 0, 0, { + // normalizeIndentation(str: string): string { + // return str.replace(/\t/g, ' '); + // } + // }); + + // assert.equal(boundSnippet.lines[1], ' test'); + // assert.equal(boundSnippet.placeHolders.length, 3); + // assert.equal(boundSnippet.finishPlaceHolderIndex, 2); + // let [first, second] = boundSnippet.placeHolders; + // assert.equal(first.occurences.length, 1); + // assert.equal(first.occurences[0].startColumn, 1); + // assert.equal(second.occurences.length, 1); + // assert.equal(second.occurences[0].startColumn, 7); + // }); + + test('issue #17989: Bad selection', () => { + + let snippet = CodeSnippet.fromTextmate('${1:HoldMeTight}'); + + let boundSnippet = snippet.bind('abc abc abc prefix3', 0, 12, { normalizeIndentation(str: string): string { return str.replace(/\t/g, ' '); } }); - assert.equal(boundSnippet.lines[1], ' test'); - assert.equal(boundSnippet.placeHolders.length, 3); - assert.equal(boundSnippet.finishPlaceHolderIndex, 2); + assert.equal(boundSnippet.lines[0], 'HoldMeTight'); + assert.equal(boundSnippet.placeHolders.length, 2); + assert.equal(boundSnippet.finishPlaceHolderIndex, 1); let [first, second] = boundSnippet.placeHolders; assert.equal(first.occurences.length, 1); - assert.equal(first.occurences[0].startColumn, 1); + assert.equal(first.occurences[0].startColumn, 13); + assert.equal(second.occurences.length, 1); - assert.equal(second.occurences[0].startColumn, 7); + assert.equal(second.occurences[0].startColumn, 24); + }); test('variables, simple', () => { From 89bf4916f5eb857a56ade7eb426d8632319ccf69 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 3 Jan 2017 11:40:29 +0100 Subject: [PATCH 037/131] better fix for #11890 --- .../editor/contrib/snippet/common/snippet.ts | 18 +++++- .../snippet/test/common/snippet.test.ts | 62 +++++++++++++------ 2 files changed, 58 insertions(+), 22 deletions(-) diff --git a/src/vs/editor/contrib/snippet/common/snippet.ts b/src/vs/editor/contrib/snippet/common/snippet.ts index 6392444d8f3..ed42ea1dacf 100644 --- a/src/vs/editor/contrib/snippet/common/snippet.ts +++ b/src/vs/editor/contrib/snippet/common/snippet.ts @@ -122,11 +122,25 @@ export class CodeSnippet implements ICodeSnippet { for (let {startLineNumber, startColumn, endLineNumber, endColumn} of originalPlaceHolder.occurences) { + if (startColumn > 1 || startLineNumber === 1) { + // placeholders that aren't at the beginning of new snippet lines + // will be moved by how many characters the indentation has been + // adjusted + startColumn = startColumn + deltaColumns[startLineNumber]; + endColumn = endColumn + deltaColumns[endLineNumber]; + + } else { + // placeholders at the beginning of new snippet lines + // will be indented by the reference indentation + startColumn += referenceIndentation.length; + endColumn += referenceIndentation.length; + } + resultOccurences.push({ startLineNumber: startLineNumber + deltaLine, - startColumn: startColumn + deltaColumns[startLineNumber], + startColumn, endLineNumber: endLineNumber + deltaLine, - endColumn: endColumn + deltaColumns[endLineNumber] + endColumn, }); } diff --git a/src/vs/editor/contrib/snippet/test/common/snippet.test.ts b/src/vs/editor/contrib/snippet/test/common/snippet.test.ts index 37177a34e87..256d778fdaa 100644 --- a/src/vs/editor/contrib/snippet/test/common/snippet.test.ts +++ b/src/vs/editor/contrib/snippet/test/common/snippet.test.ts @@ -225,29 +225,51 @@ suite('Editor Contrib - Snippets', () => { }); - // test('issue #11890: Bad cursor position', () => { + test('issue #11890: Bad cursor position 1/2', () => { - // let snippet = CodeSnippet.fromTextmate([ - // 'afterEach((done) => {', - // '${1}\ttest${2}', - // '})' - // ].join('\n')); + let snippet = CodeSnippet.fromTextmate([ + 'afterEach((done) => {', + '${1}\ttest${2}', + '})' + ].join('\n')); - // let boundSnippet = snippet.bind('', 0, 0, { - // normalizeIndentation(str: string): string { - // return str.replace(/\t/g, ' '); - // } - // }); + let boundSnippet = snippet.bind('', 0, 0, { + normalizeIndentation(str: string): string { + return str.replace(/\t/g, ' '); + } + }); - // assert.equal(boundSnippet.lines[1], ' test'); - // assert.equal(boundSnippet.placeHolders.length, 3); - // assert.equal(boundSnippet.finishPlaceHolderIndex, 2); - // let [first, second] = boundSnippet.placeHolders; - // assert.equal(first.occurences.length, 1); - // assert.equal(first.occurences[0].startColumn, 1); - // assert.equal(second.occurences.length, 1); - // assert.equal(second.occurences[0].startColumn, 7); - // }); + assert.equal(boundSnippet.lines[1], ' test'); + assert.equal(boundSnippet.placeHolders.length, 3); + assert.equal(boundSnippet.finishPlaceHolderIndex, 2); + + let [first, second] = boundSnippet.placeHolders; + assert.equal(first.occurences.length, 1); + assert.equal(first.occurences[0].startColumn, 1); + assert.equal(second.occurences.length, 1); + assert.equal(second.occurences[0].startColumn, 7); + }); + + test('issue #11890: Bad cursor position 2/2', () => { + + let snippet = CodeSnippet.fromTextmate('${1}\ttest'); + + let boundSnippet = snippet.bind('abc abc abc prefix3', 0, 12, { + normalizeIndentation(str: string): string { + return str.replace(/\t/g, ' '); + } + }); + + assert.equal(boundSnippet.lines[0], '\ttest'); + assert.equal(boundSnippet.placeHolders.length, 2); + assert.equal(boundSnippet.finishPlaceHolderIndex, 1); + + let [first, second] = boundSnippet.placeHolders; + assert.equal(first.occurences.length, 1); + assert.equal(first.occurences[0].startColumn, 13); + assert.equal(second.occurences.length, 1); + assert.equal(second.occurences[0].startColumn, 18); + }); test('issue #17989: Bad selection', () => { From 5ca45ab74461de4451732c8e873c2ecd1b8e6d8f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 3 Jan 2017 12:16:45 +0100 Subject: [PATCH 038/131] Revert "fix #15395" This reverts commit 58ab5ecdd9e54643e3ae9ae188021844329c0854. --- .../contextmenu/browser/contextmenu.ts | 15 ++++++++--- .../actions/browser/menuItemActionItem.ts | 25 +------------------ .../browser/parts/editor/titleControl.ts | 2 +- 3 files changed, 13 insertions(+), 29 deletions(-) diff --git a/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts index 2c6ebab2d31..326b615d401 100644 --- a/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts @@ -11,12 +11,11 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; -import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { ICommonCodeEditor, IEditorContribution, MouseTargetType, EditorContextKeys, IScrollEvent } from 'vs/editor/common/editorCommon'; import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; import { ICodeEditor, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; @@ -125,9 +124,17 @@ export class ContextMenuController implements IEditorContribution { private _getMenuActions(): IAction[] { const result: IAction[] = []; - const contextMenu = this._menuService.createMenu(MenuId.EditorContext, this._contextKeyService); - fillInActions(contextMenu, this._editor.getModel().uri, result); + + let contextMenu = this._menuService.createMenu(MenuId.EditorContext, this._contextKeyService); + const groups = contextMenu.getActions(this._editor.getModel().uri); contextMenu.dispose(); + + for (let group of groups) { + const [, actions] = group; + result.push(...actions); + result.push(new Separator()); + } + result.pop(); // remove last separator return result; } diff --git a/src/vs/platform/actions/browser/menuItemActionItem.ts b/src/vs/platform/actions/browser/menuItemActionItem.ts index 39d641b2ecb..64152cc193b 100644 --- a/src/vs/platform/actions/browser/menuItemActionItem.ts +++ b/src/vs/platform/actions/browser/menuItemActionItem.ts @@ -17,7 +17,7 @@ import { domEvent } from 'vs/base/browser/event'; import { Emitter } from 'vs/base/common/event'; -export function fillInActions(menu: IMenu, context: any, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, ignoreAltKey?: boolean): void { +export function fillInActions(menu: IMenu, context: any, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }): void { const groups = menu.getActions(context); if (groups.length === 0) { return; @@ -25,11 +25,6 @@ export function fillInActions(menu: IMenu, context: any, target: IAction[] | { p for (let tuple of groups) { let [group, actions] = tuple; - - if (!ignoreAltKey && _altKey.value) { - swapWithAltActionsIfPossible(actions); - } - if (group === 'navigation') { const head = Array.isArray(target) ? target : target.primary; @@ -67,13 +62,6 @@ export function fillInActions(menu: IMenu, context: any, target: IAction[] | { p } } -function swapWithAltActionsIfPossible(actions: MenuItemAction[]): void { - for (let i = 0; i < actions.length; i++) { - if (actions[i].alt) { - actions[i] = actions[i].alt; - } - } -} export function createActionItem(action: IAction, keybindingService: IKeybindingService, messageService: IMessageService): ActionItem { if (action instanceof MenuItemAction) { @@ -86,8 +74,6 @@ const _altKey = new class extends Emitter { private _subscriptions: IDisposable[] = []; - private _value = false; - constructor() { super(); @@ -97,15 +83,6 @@ const _altKey = new class extends Emitter { this._subscriptions.push(domEvent(document.body, 'blur')(e => this.fire(false))); } - fire(value: boolean) { - super.fire(value); - this._value = value; - } - - get value() { - return this._value; - } - dispose() { super.dispose(); this._subscriptions = dispose(this._subscriptions); diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index 716e65cb6ac..bc9e500d594 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -323,7 +323,7 @@ export abstract class TitleControl implements ITitleAreaControl { const titleBarMenu = this.menuService.createMenu(MenuId.EditorTitle, scopedContextKeyService); this.disposeOnEditorActions.push(titleBarMenu, titleBarMenu.onDidChange(_ => this.update())); - fillInActions(titleBarMenu, this.resourceContext.get(), { primary, secondary }, true); + fillInActions(titleBarMenu, this.resourceContext.get(), { primary, secondary }); } return { primary, secondary }; From 7b9d1c6a7bdc769b06bb548a1b6f602ced35f82e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 3 Jan 2017 14:02:11 +0100 Subject: [PATCH 039/131] add quickFixModel-tests in preparation for #16911 --- .../quickFix/browser/lightBulbWidget.ts | 2 +- .../contrib/quickFix/browser/quickFix.ts | 6 +- .../{browser => common}/quickFixModel.ts | 30 ++--- .../test/common/quickFixModel.test.ts | 123 ++++++++++++++++++ 4 files changed, 141 insertions(+), 20 deletions(-) rename src/vs/editor/contrib/quickFix/{browser => common}/quickFixModel.ts (90%) create mode 100644 src/vs/editor/contrib/quickFix/test/common/quickFixModel.test.ts diff --git a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts index 8962a91ff0c..5a571f79eb7 100644 --- a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts +++ b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts @@ -9,7 +9,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import Event, { Emitter, any } from 'vs/base/common/event'; import * as dom from 'vs/base/browser/dom'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; -import { QuickFixComputeEvent } from './quickFixModel'; +import { QuickFixComputeEvent } from 'vs/editor/contrib/quickFix/common/quickFixModel'; export class LightBulbWidget implements IOverlayWidget, IDisposable { diff --git a/src/vs/editor/contrib/quickFix/browser/quickFix.ts b/src/vs/editor/contrib/quickFix/browser/quickFix.ts index e6ba08288a4..26788738486 100644 --- a/src/vs/editor/contrib/quickFix/browser/quickFix.ts +++ b/src/vs/editor/contrib/quickFix/browser/quickFix.ts @@ -16,9 +16,9 @@ import { ICommonCodeEditor, EditorContextKeys, ModeContextKeys, IEditorContribut import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; -import { QuickFixContextMenu } from './quickFixWidget'; -import { LightBulbWidget } from './lightBulbWidget'; -import { QuickFixModel, QuickFixComputeEvent } from './quickFixModel'; +import { QuickFixContextMenu } from 'vs/editor/contrib/quickFix/browser/quickFixWidget'; +import { LightBulbWidget } from 'vs/editor/contrib/quickFix/browser/lightBulbWidget'; +import { QuickFixModel, QuickFixComputeEvent } from 'vs/editor/contrib/quickFix/common/quickFixModel'; @editorContribution export class QuickFixController implements IEditorContribution { diff --git a/src/vs/editor/contrib/quickFix/browser/quickFixModel.ts b/src/vs/editor/contrib/quickFix/common/quickFixModel.ts similarity index 90% rename from src/vs/editor/contrib/quickFix/browser/quickFixModel.ts rename to src/vs/editor/contrib/quickFix/common/quickFixModel.ts index 1890ae127fa..dc144db9c11 100644 --- a/src/vs/editor/contrib/quickFix/browser/quickFixModel.ts +++ b/src/vs/editor/contrib/quickFix/common/quickFixModel.ts @@ -13,14 +13,14 @@ import { IMarker, IMarkerService } from 'vs/platform/markers/common/markers'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ICommonCodeEditor, IPosition, IRange } from 'vs/editor/common/editorCommon'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { CodeActionProviderRegistry, CodeAction } from 'vs/editor/common/modes'; import { getCodeActions } from '../common/quickFix'; -class QuickFixOracle { +export class QuickFixOracle { private _disposables: IDisposable[] = []; + private _currentRange: IRange; constructor(private _editor: ICommonCodeEditor, private _markerService: IMarkerService, private _signalChange: (e: QuickFixComputeEvent) => any) { @@ -36,27 +36,25 @@ class QuickFixOracle { private _onMarkerChanges(resources: URI[]): void { const {uri} = this._editor.getModel(); - let affectedBy = false; for (const resource of resources) { if (resource.toString() === uri.toString()) { - affectedBy = true; - break; + this._onCursorChange(); + return; } } - if (affectedBy) { - this._onCursorChange(); - } } private _onCursorChange(): void { const range = this._markerAtPosition() || this._wordAtPosition(); - - this._signalChange({ - type: 'auto', - range, - position: this._editor.getPosition(), - fixes: range && getCodeActions(this._editor.getModel(), this._editor.getModel().validateRange(range)) - }); + if (!Range.equalsRange(this._currentRange, range)) { + this._currentRange = range; + this._signalChange({ + type: 'auto', + range, + position: this._editor.getPosition(), + fixes: range && getCodeActions(this._editor.getModel(), this._editor.getModel().validateRange(range)) + }); + } } private _markerAtPosition(): IMarker { @@ -109,7 +107,7 @@ export class QuickFixModel { private _onDidChangeFixes = new Emitter(); private _disposables: IDisposable[] = []; - constructor(editor: ICodeEditor, markerService: IMarkerService) { + constructor(editor: ICommonCodeEditor, markerService: IMarkerService) { this._editor = editor; this._markerService = markerService; diff --git a/src/vs/editor/contrib/quickFix/test/common/quickFixModel.test.ts b/src/vs/editor/contrib/quickFix/test/common/quickFixModel.test.ts new file mode 100644 index 00000000000..f0def9ea637 --- /dev/null +++ b/src/vs/editor/contrib/quickFix/test/common/quickFixModel.test.ts @@ -0,0 +1,123 @@ +/*--------------------------------------------------------------------------------------------- + * 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 assert from 'assert'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Model } from 'vs/editor/common/model/model'; +import { mockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import { MarkerService } from 'vs/platform/markers/common/markerService'; +import { QuickFixOracle } from 'vs/editor/contrib/quickFix/common/quickFixModel'; +import { CodeActionProviderRegistry } from 'vs/editor/common/modes'; + + +suite('QuickFix', () => { + + let uri = URI.parse('fake:path'); + let model = Model.createFromString('foobar\nfarboo', undefined, 'foo-lang', uri); + let markerService: MarkerService; + let editor: ICommonCodeEditor; + + CodeActionProviderRegistry.register('foo-lang', { + provideCodeActions() { + return [{ command: { id: 'test-command', title: 'test', arguments: [] }, score: 1 }]; + } + }); + + setup(() => { + markerService = new MarkerService(); + editor = mockCodeEditor([], { model }); + editor.setPosition({ lineNumber: 1, column: 1 }); + }); + + test('Orcale -> marker added', done => { + + const oracle = new QuickFixOracle(editor, markerService, e => { + assert.equal(e.type, 'auto'); + assert.ok(e.fixes); + + e.fixes.then(fixes => { + oracle.dispose(); + assert.equal(fixes.length, 1); + done(); + }, done); + }); + + // start here + markerService.changeOne('fake', uri, [{ + startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6, + message: 'error', + severity: 1, + code: '', + source: '' + }]); + + }); + + test('Orcale -> position changed', done => { + + markerService.changeOne('fake', uri, [{ + startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6, + message: 'error', + severity: 1, + code: '', + source: '' + }]); + + editor.setPosition({ lineNumber: 2, column: 1 }); + + const oracle = new QuickFixOracle(editor, markerService, e => { + assert.equal(e.type, 'auto'); + assert.ok(e.fixes); + + e.fixes.then(fixes => { + oracle.dispose(); + assert.equal(fixes.length, 1); + done(); + }, done); + }); + + // start here + editor.setPosition({ lineNumber: 1, column: 1 }); + + }); + + test('Oracle -> ask once per marker', () => { + let counter = 0; + let reg = CodeActionProviderRegistry.register('foo-lang', { + provideCodeActions() { + counter += 1; + return []; + } + }); + + markerService.changeOne('fake', uri, [{ + startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6, + message: 'error', + severity: 1, + code: '', + source: '' + }]); + + let fixes: TPromise[] = []; + let oracle = new QuickFixOracle(editor, markerService, e => { + fixes.push(e.fixes); + }); + + + editor.setPosition({ lineNumber: 1, column: 3 }); // marker + editor.setPosition({ lineNumber: 1, column: 6 }); // marker + editor.setPosition({ lineNumber: 2, column: 2 }); // no marker + + return TPromise.join(fixes).then(_ => { + reg.dispose(); + oracle.dispose(); + assert.equal(counter, 1); + }); + }); + +}); From 406bf9415469002c7625bc9cd431923b672860c2 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 3 Jan 2017 15:20:19 +0100 Subject: [PATCH 040/131] zenMode configuraitons --- .../browser/actions/toggleZenMode.ts | 6 ++--- .../electron-browser/main.contribution.ts | 27 ++++++++++++++++++- .../workbench/electron-browser/workbench.ts | 22 ++++++++------- .../services/part/common/partService.ts | 9 +------ 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/browser/actions/toggleZenMode.ts b/src/vs/workbench/browser/actions/toggleZenMode.ts index 01718c2a6cd..d542e39b8ee 100644 --- a/src/vs/workbench/browser/actions/toggleZenMode.ts +++ b/src/vs/workbench/browser/actions/toggleZenMode.ts @@ -10,7 +10,7 @@ import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/platform'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry'; -import { IPartService, IZenModeOptions } from 'vs/workbench/services/part/common/partService'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; class ToggleZenMode extends Action { @@ -26,8 +26,8 @@ class ToggleZenMode extends Action { this.enabled = !!this.partService; } - public run(options: IZenModeOptions): TPromise { - this.partService.toggleZenMode(options); + public run(): TPromise { + this.partService.toggleZenMode(); return TPromise.as(null); } } diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index f3b8f38466e..8be72f39727 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -248,4 +248,29 @@ configurationRegistry.registerConfiguration({ 'title': nls.localize('windowConfigurationTitle', "Window"), 'type': 'object', 'properties': properties -}); \ No newline at end of file +}); + +// Configuration: Zen Mode +configurationRegistry.registerConfiguration({ + 'id': 'zenMode', + 'order': 9, + 'title': nls.localize('zenModeConfigurationTitle', "Zen Mode"), + 'type': 'object', + 'properties': { + 'zenMode.fullScreen': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('zenMode.fullScreen', "Controls if turning on Zen Mode also puts the workbench into full screen mode.") + }, + 'zenMode.hideTabs': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('zenMode.hideTabs', "Controls if turning on Zen Mode also hides workbench tabs.") + }, + 'zenMode.hideStatusBar': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('zenMode.hideStatusBar', "Controls if turning on Zen Mode also hides the status bar at the bottom of the workbench.") + } + } +}); diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 10e91fab40a..17c34c12ed0 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -41,7 +41,7 @@ import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickO import { getServices } from 'vs/platform/instantiation/common/extensions'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { WorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService'; -import { Position, Parts, IPartService, ILayoutOptions, IZenModeOptions } from 'vs/workbench/services/part/common/partService'; +import { Position, Parts, IPartService, ILayoutOptions } from 'vs/workbench/services/part/common/partService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ContextMenuService } from 'vs/workbench/services/contextview/electron-browser/contextmenuService'; @@ -97,6 +97,12 @@ interface WorkbenchParams { serviceCollection: ServiceCollection; } +interface IZenModeSettings { + fullScreen: boolean; + hideTabs: boolean; + hideStatusBar: boolean; +} + export interface IWorkbenchStartedInfo { customKeybindingsCount: number; restoreViewletDuration: number; @@ -1049,26 +1055,24 @@ export class Workbench implements IPartService { return Identifiers.WORKBENCH_CONTAINER; } - public toggleZenMode(options?: IZenModeOptions): void { - options = options || {}; + public toggleZenMode(): void { this.zenMode.active = !this.zenMode.active; // Check if zen mode transitioned to full screen and if now we are out of zen mode -> we need to go out of full screen let toggleFullScreen = false; if (this.zenMode.active) { - toggleFullScreen = !browser.isFullscreen() && !options.noFullScreen; + const config = this.configurationService.getConfiguration('zenMode'); + toggleFullScreen = !browser.isFullscreen() && config.fullScreen; this.zenMode.transitionedToFullScreen = toggleFullScreen; this.zenMode.wasSideBarVisible = this.isVisible(Parts.SIDEBAR_PART); this.zenMode.wasPanelVisible = this.isVisible(Parts.PANEL_PART); this.setPanelHidden(true, true); this.setSideBarHidden(true, true); - if (!options.keepStatusBar) { + this.setActivityBarHidden(true, true); + if (config.hideStatusBar) { this.setStatusBarHidden(true, true); } - if (!options.keepActivityBar) { - this.setActivityBarHidden(true, true); - } - if (!options.keepTabs) { + if (config.hideTabs) { this.editorPart.hideTabs(true); } } else { diff --git a/src/vs/workbench/services/part/common/partService.ts b/src/vs/workbench/services/part/common/partService.ts index 4a0475f01eb..5036d29cf88 100644 --- a/src/vs/workbench/services/part/common/partService.ts +++ b/src/vs/workbench/services/part/common/partService.ts @@ -27,13 +27,6 @@ export interface ILayoutOptions { toggleMaximizedPanel?: boolean; } -export interface IZenModeOptions { - noFullScreen?: boolean; - keepStatusBar?: boolean; - keepTabs?: boolean; - keepActivityBar?: boolean; -} - export const IPartService = createDecorator('partService'); export interface IPartService { @@ -123,5 +116,5 @@ export interface IPartService { /** * Toggles the workbench in and out of zen mode - parts get hidden and window goes fullscreen. */ - toggleZenMode(options?: IZenModeOptions): void; + toggleZenMode(): void; } \ No newline at end of file From 4ef44123a109f775b8bc3d768d8227a0ac5066a5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 3 Jan 2017 15:24:52 +0100 Subject: [PATCH 041/131] better sequentialize saving file models (for #18037) --- .../textfile/common/textFileEditorModel.ts | 96 ++++++++++++------- 1 file changed, 61 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 76121a70a28..5bd861a9513 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -56,7 +56,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private autoSaveAfterMilliesEnabled: boolean; private autoSavePromise: TPromise; private contentChangeEventScheduler: RunOnceScheduler; - private mapPendingSaveToVersionId: { [versionId: string]: TPromise }; + private saveSequentalizer: SaveSequentalizer; private disposed: boolean; private inConflictResolutionMode: boolean; private inErrorMode: boolean; @@ -92,7 +92,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.dirty = false; this.versionId = 0; this.lastSaveAttemptTime = 0; - this.mapPendingSaveToVersionId = {}; + this.saveSequentalizer = new SaveSequentalizer(); this.contentChangeEventScheduler = new RunOnceScheduler(() => this._onDidContentChange.fire(StateChange.CONTENT_CHANGE), TextFileEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY); this.toDispose.push(this.contentChangeEventScheduler); @@ -490,7 +490,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Only trigger save if the version id has not changed meanwhile if (versionId === this.versionId) { - this.doSave(versionId, SaveReason.AUTO); // Very important here to not return the promise because if the timeout promise is canceled it will bubble up the error otherwise - do not change + this.doSave(versionId, SaveReason.AUTO).done(null, onUnexpectedError); // Very important here to not return the promise because if the timeout promise is canceled it will bubble up the error otherwise - do not change } }); @@ -528,11 +528,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Scenario: user invoked the save action multiple times quickly for the same contents // while the save was not yet finished to disk // - const pendingSave = this.mapPendingSaveToVersionId[versionId]; - if (pendingSave) { + if (this.saveSequentalizer.hasPendingSave(versionId)) { diag(`doSave(${versionId}) - exit - found a pending save for versionId ${versionId}`, this.resource, new Date()); - return pendingSave; + return this.saveSequentalizer.pendingSave; } // Return early if not dirty or version changed meanwhile @@ -548,17 +547,19 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return TPromise.as(null); } - // Return if currently saving by scheduling another auto save. Never ever must 2 saves execute at the same time because - // this can lead to dirty writes and race conditions + // Return if currently saving by scheduling another auto save if enabled or storing this version id as next save. + // Never ever must 2 saves execute at the same time because this can lead to dirty writes and race conditions. // - // Scenario: auto save was triggered and is currently busy saving to disk. this takes long enough that another auto save - // kicks in. since we never want to trigger 2 saves at the same time, we push out this auto save for the - // configured auto save delay assuming that it can proceed next time it triggers. + // Scenario A: auto save was triggered and is currently busy saving to disk. this takes long enough that another auto save + // kicks in. since we never want to trigger 2 saves at the same time, we push out this auto save for the + // configured auto save delay assuming that it can proceed next time it triggers. + // Scenario B: save is very slow (e.g. network share) and the user manages to change the buffer and trigger another save + // while the first save has not returned yet. // - if (this.isBusySaving()) { + if (this.saveSequentalizer.hasPendingSave()) { diag(`doSave(${versionId}) - exit - because busy saving`, this.resource, new Date()); - // Avoid endless loop here and guard if auto save is disabled + // Trigger another auto save if enabled if (this.autoSaveAfterMilliesEnabled) { return this.doAutoSave(versionId); } @@ -590,11 +591,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil }).then(onCompleteOrError, onCompleteOrError); } - this.mapPendingSaveToVersionId[versionId] = saveParticipantPromise.then(newVersionId => { + // mark the save participant as current pending save operation + return this.saveSequentalizer.setPending(versionId, saveParticipantPromise.then(newVersionId => { - // remove save participant promise from pending saves and update versionId with - // its new value (if pre-save changes happened) - delete this.mapPendingSaveToVersionId[versionId]; + // update versionId with its new value (if pre-save changes happened) versionId = newVersionId; // Clear error flag since we are trying to save again @@ -604,8 +604,9 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.lastSaveAttemptTime = Date.now(); // Save to Disk + // mark the save operation as currently pending with the versionId (it might have changed from a save participant triggering) diag(`doSave(${versionId}) - before updateContent()`, this.resource, new Date()); - this.mapPendingSaveToVersionId[versionId] = this.fileService.updateContent(this.versionOnDiskStat.resource, this.getValue(), { + return this.saveSequentalizer.setPending(newVersionId, this.fileService.updateContent(this.versionOnDiskStat.resource, this.getValue(), { overwriteReadonly, overwriteEncoding, mtime: this.versionOnDiskStat.mtime, @@ -614,9 +615,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil }).then((stat: IFileStat) => { diag(`doSave(${versionId}) - after updateContent()`, this.resource, new Date()); - // Remove from pending saves - delete this.mapPendingSaveToVersionId[versionId]; - // Telemetry this.telemetryService.publicLog('filePUT', { mimeType: guessMimeTypes(this.resource.fsPath).join(', '), ext: paths.extname(this.versionOnDiskStat.resource.fsPath) }); @@ -639,9 +637,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil }, error => { diag(`doSave(${versionId}) - exit - resulted in a save error: ${error.toString()}`, this.resource, new Date()); - // Remove from pending saves - delete this.mapPendingSaveToVersionId[versionId]; - // Flag as error state this.inErrorMode = true; @@ -650,12 +645,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Emit as event this._onDidStateChange.fire(StateChange.SAVE_ERROR); - }); - - return this.mapPendingSaveToVersionId[versionId]; - }); - - return this.mapPendingSaveToVersionId[versionId]; + })); + })); } private setDirty(dirty: boolean): () => void { @@ -753,7 +744,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return ModelState.SAVED; } - if (this.isBusySaving()) { + if (this.saveSequentalizer.hasPendingSave()) { return ModelState.PENDING_SAVE; } @@ -762,10 +753,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - private isBusySaving(): boolean { - return !types.isEmptyObject(this.mapPendingSaveToVersionId); - } - public getEncoding(): string { return this.preferredEncoding || this.contentEncoding; } @@ -861,6 +848,45 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } +interface IPendingSave { + versionId: number; + promise: TPromise; +} + +class SaveSequentalizer { + private _pendingSave: IPendingSave; + + public hasPendingSave(versionId?: number): boolean { + if (!this._pendingSave) { + return false; + } + + if (typeof versionId === 'number') { + return this._pendingSave.versionId === versionId; + } + + return !!this._pendingSave; + } + + public setPending(versionId: number, promise: TPromise): TPromise { + this._pendingSave = { versionId, promise }; + + promise.done(() => this.donePending(versionId), () => this.donePending(versionId)); + + return promise; + } + + private donePending(versionId: number): void { + if (this._pendingSave && versionId === this._pendingSave.versionId) { + this._pendingSave = void 0; // only set pending to done if the promise finished that is associated with that versionId + } + } + + public get pendingSave(): TPromise { + return this._pendingSave ? this._pendingSave.promise : void 0; + } +} + class DefaultSaveErrorHandler implements ISaveErrorHandler { constructor( @IMessageService private messageService: IMessageService) { } From 559cbf67d84ce23d571b91327d4b666a65854255 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 3 Jan 2017 14:56:38 +0100 Subject: [PATCH 042/131] add suiteTeardown --- .../contrib/quickFix/test/common/quickFixModel.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/quickFix/test/common/quickFixModel.test.ts b/src/vs/editor/contrib/quickFix/test/common/quickFixModel.test.ts index f0def9ea637..9ea53e8a88a 100644 --- a/src/vs/editor/contrib/quickFix/test/common/quickFixModel.test.ts +++ b/src/vs/editor/contrib/quickFix/test/common/quickFixModel.test.ts @@ -22,7 +22,7 @@ suite('QuickFix', () => { let markerService: MarkerService; let editor: ICommonCodeEditor; - CodeActionProviderRegistry.register('foo-lang', { + let reg = CodeActionProviderRegistry.register('foo-lang', { provideCodeActions() { return [{ command: { id: 'test-command', title: 'test', arguments: [] }, score: 1 }]; } @@ -34,6 +34,11 @@ suite('QuickFix', () => { editor.setPosition({ lineNumber: 1, column: 1 }); }); + suiteTeardown(() => { + reg.dispose(); + model.dispose(); + }); + test('Orcale -> marker added', done => { const oracle = new QuickFixOracle(editor, markerService, e => { From 6b4611f2216eb97b288536057d57b4c942eb9998 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 3 Jan 2017 15:38:00 +0100 Subject: [PATCH 043/131] ask for quick fixes when navigating inside works, #16911 --- .../contrib/quickFix/browser/quickFix.ts | 2 +- .../contrib/quickFix/common/quickFixModel.ts | 77 ++++++++++++------- .../test/common/quickFixModel.test.ts | 13 ++-- 3 files changed, 57 insertions(+), 35 deletions(-) diff --git a/src/vs/editor/contrib/quickFix/browser/quickFix.ts b/src/vs/editor/contrib/quickFix/browser/quickFix.ts index 26788738486..f963b16ee90 100644 --- a/src/vs/editor/contrib/quickFix/browser/quickFix.ts +++ b/src/vs/editor/contrib/quickFix/browser/quickFix.ts @@ -88,7 +88,7 @@ export class QuickFixController implements IEditorContribution { } public triggerFromEditorSelection(): void { - this._model.triggerManual(this._editor.getSelection()); + this._model.triggerManual(); } private _updateLightBulbTitle(): void { diff --git a/src/vs/editor/contrib/quickFix/common/quickFixModel.ts b/src/vs/editor/contrib/quickFix/common/quickFixModel.ts index dc144db9c11..eb680670dff 100644 --- a/src/vs/editor/contrib/quickFix/common/quickFixModel.ts +++ b/src/vs/editor/contrib/quickFix/common/quickFixModel.ts @@ -6,12 +6,12 @@ import * as arrays from 'vs/base/common/arrays'; import Event, { Emitter } from 'vs/base/common/event'; +import Severity from 'vs/base/common/severity'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { IMarker, IMarkerService } from 'vs/platform/markers/common/markers'; import { Range } from 'vs/editor/common/core/range'; -import { Selection } from 'vs/editor/common/core/selection'; import { ICommonCodeEditor, IPosition, IRange } from 'vs/editor/common/editorCommon'; import { CodeActionProviderRegistry, CodeAction } from 'vs/editor/common/modes'; import { getCodeActions } from '../common/quickFix'; @@ -34,6 +34,21 @@ export class QuickFixOracle { this._disposables = dispose(this._disposables); } + trigger(): void { + let {range, severity} = this._rangeAtPosition(); + if (!range) { + range = this._editor.getSelection(); + } + this._signalChange({ + type: 'manual', + severity, + range, + position: this._editor.getPosition(), + fixes: range && getCodeActions(this._editor.getModel(), this._editor.getModel().validateRange(range)) + }); + + } + private _onMarkerChanges(resources: URI[]): void { const {uri} = this._editor.getModel(); for (const resource of resources) { @@ -45,11 +60,12 @@ export class QuickFixOracle { } private _onCursorChange(): void { - const range = this._markerAtPosition() || this._wordAtPosition(); + const {range, severity} = this._rangeAtPosition(); if (!Range.equalsRange(this._currentRange, range)) { this._currentRange = range; this._signalChange({ type: 'auto', + severity, range, position: this._editor.getPosition(), fixes: range && getCodeActions(this._editor.getModel(), this._editor.getModel().validateRange(range)) @@ -57,6 +73,20 @@ export class QuickFixOracle { } } + private _rangeAtPosition(): { range: IRange, severity: Severity; } { + let range: IRange; + let severity: Severity; + const marker = this._markerAtPosition(); + if (marker) { + range = Range.lift(marker); + severity = marker.severity; + } else { + range = this._wordAtPosition(); + severity = Severity.Info; + } + return { range, severity }; + } + private _markerAtPosition(): IMarker { const position = this._editor.getPosition(); @@ -74,26 +104,23 @@ export class QuickFixOracle { } private _wordAtPosition(): IRange { - return; - // todo@joh - enable once we decide to eagerly show the - // light bulb as the cursor moves - // const {positionLineNumber, positionColumn} = this._editor.getSelection(); - // const model = this._editor.getModel(); - - // const info = model.getWordAtPosition({ lineNumber: positionLineNumber, column: positionColumn }); - // if (info) { - // return { - // startLineNumber: positionLineNumber, - // startColumn: info.startColumn, - // endLineNumber: positionLineNumber, - // endColumn: info.endColumn - // }; - // } + const {positionLineNumber, positionColumn} = this._editor.getSelection(); + const model = this._editor.getModel(); + const info = model.getWordAtPosition({ lineNumber: positionLineNumber, column: positionColumn }); + if (info) { + return { + startLineNumber: positionLineNumber, + startColumn: info.startColumn, + endLineNumber: positionLineNumber, + endColumn: info.endColumn + }; + } } } export interface QuickFixComputeEvent { type: 'auto' | 'manual'; + severity: Severity; range: IRange; position: IPosition; fixes: TPromise; @@ -130,7 +157,8 @@ export class QuickFixModel { private _update(): void { if (this._quickFixOracle) { - dispose(this._quickFixOracle); + this._quickFixOracle.dispose(); + this._quickFixOracle = undefined; this._onDidChangeFixes.fire(undefined); } @@ -142,16 +170,9 @@ export class QuickFixModel { } } - triggerManual(selection: Selection): void { - const model = this._editor.getModel(); - if (model) { - const fixes = getCodeActions(model, selection); - this._onDidChangeFixes.fire({ - type: 'manual', - range: selection, - position: { lineNumber: selection.positionLineNumber, column: selection.positionColumn }, - fixes - }); + triggerManual(): void { + if (this._quickFixOracle) { + this._quickFixOracle.trigger(); } } } diff --git a/src/vs/editor/contrib/quickFix/test/common/quickFixModel.test.ts b/src/vs/editor/contrib/quickFix/test/common/quickFixModel.test.ts index 9ea53e8a88a..570ab73830c 100644 --- a/src/vs/editor/contrib/quickFix/test/common/quickFixModel.test.ts +++ b/src/vs/editor/contrib/quickFix/test/common/quickFixModel.test.ts @@ -18,7 +18,7 @@ import { CodeActionProviderRegistry } from 'vs/editor/common/modes'; suite('QuickFix', () => { let uri = URI.parse('fake:path'); - let model = Model.createFromString('foobar\nfarboo', undefined, 'foo-lang', uri); + let model = Model.createFromString('foobar foo bar\nfarboo far boo', undefined, 'foo-lang', uri); let markerService: MarkerService; let editor: ICommonCodeEditor; @@ -91,7 +91,7 @@ suite('QuickFix', () => { }); - test('Oracle -> ask once per marker', () => { + test('Oracle -> ask once per marker/word', () => { let counter = 0; let reg = CodeActionProviderRegistry.register('foo-lang', { provideCodeActions() { @@ -113,15 +113,16 @@ suite('QuickFix', () => { fixes.push(e.fixes); }); - editor.setPosition({ lineNumber: 1, column: 3 }); // marker - editor.setPosition({ lineNumber: 1, column: 6 }); // marker - editor.setPosition({ lineNumber: 2, column: 2 }); // no marker + editor.setPosition({ lineNumber: 1, column: 6 }); // (same) marker + editor.setPosition({ lineNumber: 1, column: 8 }); // whitespace + editor.setPosition({ lineNumber: 2, column: 2 }); // word + editor.setPosition({ lineNumber: 2, column: 6 }); // (same) word return TPromise.join(fixes).then(_ => { reg.dispose(); oracle.dispose(); - assert.equal(counter, 1); + assert.equal(counter, 2); }); }); From 973c15e01f246d9469582e688fd4603798bf7a0f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 3 Jan 2017 15:54:16 +0100 Subject: [PATCH 044/131] sequentialize writes to files (fixes #18037) --- .../textfile/common/textFileEditorModel.ts | 83 ++++++++++++++--- .../textfile/test/textFileEditorModel.test.ts | 93 ++++++++++++++++++- 2 files changed, 162 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 5bd861a9513..e8c3d489d3d 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -6,7 +6,7 @@ import nls = require('vs/nls'); import Event, { Emitter } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { TPromise, TValueCallback, ErrorCallback } from 'vs/base/common/winjs.base'; import { onUnexpectedError } from 'vs/base/common/errors'; import { guessMimeTypes } from 'vs/base/common/mime'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -56,7 +56,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private autoSaveAfterMilliesEnabled: boolean; private autoSavePromise: TPromise; private contentChangeEventScheduler: RunOnceScheduler; - private saveSequentalizer: SaveSequentalizer; + private saveSequentializer: SaveSequentializer; private disposed: boolean; private inConflictResolutionMode: boolean; private inErrorMode: boolean; @@ -92,7 +92,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.dirty = false; this.versionId = 0; this.lastSaveAttemptTime = 0; - this.saveSequentalizer = new SaveSequentalizer(); + this.saveSequentializer = new SaveSequentializer(); this.contentChangeEventScheduler = new RunOnceScheduler(() => this._onDidContentChange.fire(StateChange.CONTENT_CHANGE), TextFileEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY); this.toDispose.push(this.contentChangeEventScheduler); @@ -528,10 +528,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Scenario: user invoked the save action multiple times quickly for the same contents // while the save was not yet finished to disk // - if (this.saveSequentalizer.hasPendingSave(versionId)) { + if (this.saveSequentializer.hasPendingSave(versionId)) { diag(`doSave(${versionId}) - exit - found a pending save for versionId ${versionId}`, this.resource, new Date()); - return this.saveSequentalizer.pendingSave; + return this.saveSequentializer.pendingSave; } // Return early if not dirty or version changed meanwhile @@ -556,13 +556,18 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Scenario B: save is very slow (e.g. network share) and the user manages to change the buffer and trigger another save // while the first save has not returned yet. // - if (this.saveSequentalizer.hasPendingSave()) { + if (this.saveSequentializer.hasPendingSave()) { diag(`doSave(${versionId}) - exit - because busy saving`, this.resource, new Date()); // Trigger another auto save if enabled if (this.autoSaveAfterMilliesEnabled) { return this.doAutoSave(versionId); } + + // Otherwise register this as the next upcoming save and return + else { + return this.saveSequentializer.setNext(() => this.doSave(versionId, reason, overwriteReadonly, overwriteEncoding)); + } } // Push all edit operations to the undo stack so that the user has a chance to @@ -592,7 +597,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // mark the save participant as current pending save operation - return this.saveSequentalizer.setPending(versionId, saveParticipantPromise.then(newVersionId => { + return this.saveSequentializer.setPending(versionId, saveParticipantPromise.then(newVersionId => { // update versionId with its new value (if pre-save changes happened) versionId = newVersionId; @@ -606,7 +611,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Save to Disk // mark the save operation as currently pending with the versionId (it might have changed from a save participant triggering) diag(`doSave(${versionId}) - before updateContent()`, this.resource, new Date()); - return this.saveSequentalizer.setPending(newVersionId, this.fileService.updateContent(this.versionOnDiskStat.resource, this.getValue(), { + return this.saveSequentializer.setPending(newVersionId, this.fileService.updateContent(this.versionOnDiskStat.resource, this.getValue(), { overwriteReadonly, overwriteEncoding, mtime: this.versionOnDiskStat.mtime, @@ -744,7 +749,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return ModelState.SAVED; } - if (this.saveSequentalizer.hasPendingSave()) { + if (this.saveSequentializer.hasPendingSave()) { return ModelState.PENDING_SAVE; } @@ -853,8 +858,16 @@ interface IPendingSave { promise: TPromise; } -class SaveSequentalizer { +interface ISaveOperation { + promise: TPromise; + promiseValue: TValueCallback; + promiseError: ErrorCallback; + run: () => TPromise; +} + +export class SaveSequentializer { private _pendingSave: IPendingSave; + private _nextSave: ISaveOperation; public hasPendingSave(versionId?: number): boolean { if (!this._pendingSave) { @@ -868,6 +881,10 @@ class SaveSequentalizer { return !!this._pendingSave; } + public get pendingSave(): TPromise { + return this._pendingSave ? this._pendingSave.promise : void 0; + } + public setPending(versionId: number, promise: TPromise): TPromise { this._pendingSave = { versionId, promise }; @@ -878,12 +895,52 @@ class SaveSequentalizer { private donePending(versionId: number): void { if (this._pendingSave && versionId === this._pendingSave.versionId) { - this._pendingSave = void 0; // only set pending to done if the promise finished that is associated with that versionId + + // only set pending to done if the promise finished that is associated with that versionId + this._pendingSave = void 0; + + // schedule the next save now that we are free if we have any + this.triggerNextSave(); } } - public get pendingSave(): TPromise { - return this._pendingSave ? this._pendingSave.promise : void 0; + private triggerNextSave(): void { + if (this._nextSave) { + const saveOperation = this._nextSave; + this._nextSave = void 0; + + // Run next save and complete on the associated promise + saveOperation.run().done(saveOperation.promiseValue, saveOperation.promiseError); + } + } + + public setNext(run: () => TPromise): TPromise { + + // this is our first next save, so we create associated promise with it + // so that we can return a promise that completes when the save operation + // has completed. + if (!this._nextSave) { + let promiseValue: TValueCallback; + let promiseError: ErrorCallback; + const promise = new TPromise((c, e) => { + promiseValue = c; + promiseError = e; + }); + + this._nextSave = { + run, + promise, + promiseValue, + promiseError + }; + } + + // we have a previous next save, just overwrite it + else { + this._nextSave.run = run; + } + + return this._nextSave.promise; } } diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts index 1edb5fa0d55..0bf4c5633d3 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts @@ -9,7 +9,7 @@ import * as assert from 'assert'; import { TPromise } from 'vs/base/common/winjs.base'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { EncodingMode } from 'vs/workbench/common/editor'; -import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; +import { TextFileEditorModel, SaveSequentializer } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { ITextFileService, ModelState, StateChange } from 'vs/workbench/services/textfile/common/textfiles'; import { workbenchInstantiationService, TestTextFileService, createFileInput } from 'vs/workbench/test/workbenchTestServices'; import { onError, toResource } from 'vs/base/test/common/utils'; @@ -376,4 +376,95 @@ suite('Files - TextFileEditorModel', () => { }); }, error => onError(error, done)); }); + + test('SaveSequentializer - pending basics', function (done) { + const sequentializer = new SaveSequentializer(); + + assert.ok(!sequentializer.hasPendingSave()); + assert.ok(!sequentializer.hasPendingSave(2323)); + assert.ok(!sequentializer.pendingSave); + + // pending removes itself after done + sequentializer.setPending(1, TPromise.as(null)); + assert.ok(!sequentializer.hasPendingSave()); + assert.ok(!sequentializer.hasPendingSave(1)); + assert.ok(!sequentializer.pendingSave); + + // pending removes itself after done (use timeout) + sequentializer.setPending(2, TPromise.timeout(1)); + assert.ok(sequentializer.hasPendingSave()); + assert.ok(sequentializer.hasPendingSave(2)); + assert.ok(!sequentializer.hasPendingSave(1)); + assert.ok(sequentializer.pendingSave); + + return TPromise.timeout(2).then(() => { + assert.ok(!sequentializer.hasPendingSave()); + assert.ok(!sequentializer.hasPendingSave(2)); + assert.ok(!sequentializer.pendingSave); + + done(); + }); + }); + + test('SaveSequentializer - pending and next (finishes instantly)', function (done) { + const sequentializer = new SaveSequentializer(); + + let pendingDone = false; + sequentializer.setPending(1, TPromise.timeout(1).then(() => { pendingDone = true; return null; })); + + // next finishes instantly + let nextDone = false; + const res = sequentializer.setNext(() => TPromise.as(null).then(() => { nextDone = true; return null; })); + + return res.done(() => { + assert.ok(pendingDone); + assert.ok(nextDone); + + done(); + }); + }); + + test('SaveSequentializer - pending and next (finishes after timeout)', function (done) { + const sequentializer = new SaveSequentializer(); + + let pendingDone = false; + sequentializer.setPending(1, TPromise.timeout(1).then(() => { pendingDone = true; return null; })); + + // next finishes after timeout + let nextDone = false; + const res = sequentializer.setNext(() => TPromise.timeout(1).then(() => { nextDone = true; return null; })); + + return res.done(() => { + assert.ok(pendingDone); + assert.ok(nextDone); + + done(); + }); + }); + + test('SaveSequentializer - pending and multiple next (last one wins)', function (done) { + const sequentializer = new SaveSequentializer(); + + let pendingDone = false; + sequentializer.setPending(1, TPromise.timeout(1).then(() => { pendingDone = true; return null; })); + + // next finishes after timeout + let firstDone = false; + let firstRes = sequentializer.setNext(() => TPromise.timeout(2).then(() => { firstDone = true; return null; })); + + let secondDone = false; + let secondRes = sequentializer.setNext(() => TPromise.timeout(3).then(() => { secondDone = true; return null; })); + + let thirdDone = false; + let thirdRes = sequentializer.setNext(() => TPromise.timeout(4).then(() => { thirdDone = true; return null; })); + + return TPromise.join([firstRes, secondRes, thirdRes]).then(() => { + assert.ok(pendingDone); + assert.ok(!firstDone); + assert.ok(!secondDone); + assert.ok(thirdDone); + + done(); + }); + }); }); \ No newline at end of file From 98d5a9d48aa022dc7298f3beb37ad94364d02a65 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 3 Jan 2017 16:26:16 +0100 Subject: [PATCH 045/131] prepare for different icon when showing light bulb for warning/error than for a word, #16911 --- .../quickFix/browser/lightBulbWidget.css | 20 +++++++++++++------ .../quickFix/browser/lightBulbWidget.ts | 7 +++++-- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.css b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.css index 457499a7e36..f94bfa6e862 100644 --- a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.css +++ b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.css @@ -8,6 +8,8 @@ display: flex; align-items: center; justify-content: center; + height: 16px; + width: 16px; } .monaco-editor .lightbulb-glyph:hover { @@ -16,12 +18,18 @@ .monaco-editor.vs .lightbulb-glyph { background: url('lightbulb.svg') center center no-repeat; - height: 16px; - width: 16px; } -.monaco-editor.vs-dark .lightbulb-glyph, .monaco-editor.hc-black .lightbulb-glyph { - background: url('lightbulb-dark.svg') center center no-repeat; - height: 16px; - width: 16px; +.monaco-editor.vs .lightbulb-glyph[data-severity="high"]{ + background: url('lightbulb.svg') center center no-repeat; +} + +.monaco-editor.vs-dark .lightbulb-glyph, +.monaco-editor.hc-black .lightbulb-glyph { + background: url('lightbulb-dark.svg') center center no-repeat; +} + +.monaco-editor.vs-dark .lightbulb-glyph[data-severity="high"], +.monaco-editor.hc-black .lightbulb-glyph[data-severity="high"] { + background: url('lightbulb-dark.svg') center center no-repeat; } diff --git a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts index 5a571f79eb7..0b46d182719 100644 --- a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts +++ b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts @@ -7,6 +7,7 @@ import 'vs/css!./lightBulbWidget'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import Event, { Emitter, any } from 'vs/base/common/event'; +import Severity from 'vs/base/common/severity'; import * as dom from 'vs/base/browser/dom'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { QuickFixComputeEvent } from 'vs/editor/contrib/quickFix/common/quickFixModel'; @@ -86,7 +87,7 @@ export class LightBulbWidget implements IOverlayWidget, IDisposable { const modelNow = this._model; e.fixes.done(fixes => { if (modelNow === this._model && fixes && fixes.length > 0) { - this.show(e.range.startLineNumber); + this.show(e); } else { this.hide(); } @@ -99,7 +100,8 @@ export class LightBulbWidget implements IOverlayWidget, IDisposable { return this._model; } - show(line: number): void { + show(e: QuickFixComputeEvent): void { + const line = e.range.startLineNumber; if (!this._hasSpaceInGlyphMargin(line)) { return; } @@ -107,6 +109,7 @@ export class LightBulbWidget implements IOverlayWidget, IDisposable { this._line = line; this._visible = true; this._layout(); + this._domNode.dataset['severity'] = e.severity >= Severity.Warning ? 'high' : ''; } } From 94e477d0b3ba3ec151e3198b70c762980f5381f3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 3 Jan 2017 18:17:20 +0100 Subject: [PATCH 046/131] :lipstick: polish dispose signature --- src/vs/base/common/lifecycle.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index 7f24172f50c..4aeaf4a743b 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -4,8 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { isArray } from './types'; - export const empty: IDisposable = Object.freeze({ dispose() { } }); @@ -14,17 +12,22 @@ export interface IDisposable { dispose(): void; } -export function dispose(...disposables: T[]): T; +export function dispose(disposable: T): T; +export function dispose(...disposables: T[]): T[]; export function dispose(disposables: T[]): T[]; -export function dispose(...disposables: T[]): T[] { - const first = disposables[0]; +export function dispose(first: T | T[], ...rest: T[]): T | T[] { - if (isArray(first)) { - disposables = first as any as T[]; + if (Array.isArray(first)) { + first.forEach(d => d && d.dispose()); + return []; + } else if (rest.length === 0 && first) { + first.dispose(); + return first; + } else { + dispose(first); + dispose(rest); + return []; } - - disposables.forEach(d => d && d.dispose()); - return []; } export function combinedDisposable(disposables: IDisposable[]): IDisposable { @@ -105,4 +108,4 @@ export abstract class ReferenceCollection { export class ImmortalReference implements IReference { constructor(public object: T) { } dispose(): void { /* noop */ } -} \ No newline at end of file +} From 8bca6d86aaa0664b121d63fc3bc262770b39a162 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 3 Jan 2017 18:39:45 +0100 Subject: [PATCH 047/131] #17646: - Search each word in description, key and value - Make sure all words are found in description, key and value - Search in values if setting is enum type --- src/vs/base/common/filters.ts | 19 ++-- .../preferences/browser/preferencesEditor.ts | 2 +- .../preferences/common/preferencesModels.ts | 103 ++++++++++++++---- 3 files changed, 94 insertions(+), 30 deletions(-) diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index ad0bc3ef508..27d0ce1e469 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -272,10 +272,11 @@ export function matchesCamelCase(word: string, camelCaseWord: string): IMatch[] } // Matches beginning of words supporting non-ASCII languages -// E.g. "gp" or "g p" will match "Git: Pull" +// If `contiguous` is true then matches word with beginnings of the words in the target. E.g. "pul" will match "Git: Pull" +// Otherwise also matches sub string of the word with beginnings of the words in the target. E.g. "gp" or "g p" will match "Git: Pull" // Useful in cases where the target is words (e.g. command labels) -export function matchesWords(word: string, target: string): IMatch[] { +export function matchesWords(word: string, target: string, contiguous: boolean = false): IMatch[] { if (!target || target.length === 0) { return null; } @@ -283,14 +284,14 @@ export function matchesWords(word: string, target: string): IMatch[] { let result: IMatch[] = null; let i = 0; - while (i < target.length && (result = _matchesWords(word.toLowerCase(), target, 0, i)) === null) { + while (i < target.length && (result = _matchesWords(word.toLowerCase(), target, 0, i, contiguous)) === null) { i = nextWord(target, i + 1); } return result; } -function _matchesWords(word: string, target: string, i: number, j: number): IMatch[] { +function _matchesWords(word: string, target: string, i: number, j: number, contiguous: boolean): IMatch[] { if (i === word.length) { return []; } else if (j === target.length) { @@ -300,10 +301,12 @@ function _matchesWords(word: string, target: string, i: number, j: number): IMat } else { let result = null; let nextWordIndex = j + 1; - result = _matchesWords(word, target, i + 1, j + 1); - while (!result && (nextWordIndex = nextWord(target, nextWordIndex)) < target.length) { - result = _matchesWords(word, target, i + 1, nextWordIndex); - nextWordIndex++; + result = _matchesWords(word, target, i + 1, j + 1, contiguous); + if (!contiguous) { + while (!result && (nextWordIndex = nextWord(target, nextWordIndex)) < target.length) { + result = _matchesWords(word, target, i + 1, nextWordIndex, contiguous); + nextWordIndex++; + } } return result === null ? null : join({ start: j, end: j + 1 }, result); } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 8d35c13e780..bed793481b3 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -190,7 +190,7 @@ export class DefaultPreferencesEditor extends BaseTextEditor { private filterPreferences(filter: string) { this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(filter)); - (this.getDefaultPreferencesContribution().getPreferencesRenderer()).filterPreferences(filter); + (this.getDefaultPreferencesContribution().getPreferencesRenderer()).filterPreferences(filter.trim()); } private focusNextPreference() { diff --git a/src/vs/workbench/parts/preferences/common/preferencesModels.ts b/src/vs/workbench/parts/preferences/common/preferencesModels.ts index e01c1dd977f..57ca716dda3 100644 --- a/src/vs/workbench/parts/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/parts/preferences/common/preferencesModels.ts @@ -12,16 +12,15 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/platform'; import { visit, JSONVisitor } from 'vs/base/common/json'; import { IModel, IRange } from 'vs/editor/common/editorCommon'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { IConfigurationNode, IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; import { ISettingsEditorModel, IKeybindingsEditorModel, ISettingsGroup, ISetting, IFilterResult, ISettingsSection } from 'vs/workbench/parts/preferences/common/preferences'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { IFilter, or, matchesContiguousSubString, matchesPrefix, /*matchesFuzzy,*/ matchesWords } from 'vs/base/common/filters'; +import { IMatch, or, matchesContiguousSubString, matchesPrefix, matchesCamelCase, matchesWords } from 'vs/base/common/filters'; export abstract class AbstractSettingsModel extends Disposable { - static _descriptionFilter: IFilter = matchesWords; - public get groupsTerms(): string[] { return this.settingsGroups.map(group => '@' + group.id); } @@ -102,35 +101,97 @@ export abstract class AbstractSettingsModel extends Disposable { } private _findMatchesInSetting(searchString: string, setting: ISetting): IRange[] { - const result: IRange[] = [...this._findMatchesInDescription(searchString, setting)]; - result.push(...this._findMatchesInSettingKey(searchString, setting)); - return result; + const registry: { [qualifiedKey: string]: IJSONSchema } = Registry.as(Extensions.Configuration).getConfigurationProperties(); + const schema: IJSONSchema = registry[setting.key]; + + let words = searchString.split(' '); + let descriptionMatchingWords: Map = new Map(); + let keyMatchingWords: Map = new Map(); + let valueMatchingWords: Map = new Map(); + const settingKeyAsWords: string = setting.key.split('.').join(' '); + + for (const word of words) { + for (let lineIndex = 0; lineIndex < setting.description.length; lineIndex++) { + const descriptionMatches = matchesWords(word, setting.description[lineIndex], true); + if (descriptionMatches) { + descriptionMatchingWords.set(word, descriptionMatches.map(match => this.toDescriptionRange(setting, match, lineIndex))); + } + } + + const keyMatches = or(matchesWords, matchesCamelCase)(word, settingKeyAsWords); + if (keyMatches) { + keyMatchingWords.set(word, keyMatches.map(match => this.toKeyRange(setting, match))); + } + + if (schema.type === 'string' || schema.enum) { + const valueMatches = matchesContiguousSubString(word, setting.value); + if (valueMatches) { + valueMatchingWords.set(word, valueMatches.map(match => this.toValueRange(setting, match))); + } else if (schema.enum && schema.enum.some(enumValue => !!matchesContiguousSubString(word, enumValue))) { + valueMatchingWords.set(word, []); + } + } + } + + const descriptionRanges: IRange[] = []; + for (let lineIndex = 0; lineIndex < setting.description.length; lineIndex++) { + const matches = or(matchesContiguousSubString)(searchString, setting.description[lineIndex]) || []; + descriptionRanges.push(...matches.map(match => this.toDescriptionRange(setting, match, lineIndex))); + } + if (descriptionRanges.length === 0) { + descriptionRanges.push(...this.getRangesForWords(words, descriptionMatchingWords, [keyMatchingWords, valueMatchingWords])); + } + + const keyMatches = or(matchesPrefix, matchesContiguousSubString)(searchString, setting.key); + const keyRanges: IRange[] = keyMatches ? keyMatches.map(match => this.toKeyRange(setting, match)) : this.getRangesForWords(words, keyMatchingWords, [descriptionMatchingWords, valueMatchingWords]); + + let valueRanges: IRange[] = []; + if (typeof setting.value === 'string') { + const valueMatches = or(matchesPrefix, matchesContiguousSubString)(searchString, setting.value); + valueRanges = valueMatches ? valueMatches.map(match => this.toValueRange(setting, match)) : this.getRangesForWords(words, valueMatchingWords, [keyMatchingWords, descriptionMatchingWords]); + } + + return [...descriptionRanges, ...keyRanges, ...valueRanges]; } - private _findMatchesInDescription(searchString: string, setting: ISetting): IRange[] { + private getRangesForWords(words: string[], from: Map, others: Map[]): IRange[] { const result: IRange[] = []; - for (var i = 0; i < setting.description.length; i++) { - var line = setting.description[i]; - const matches = matchesContiguousSubString(searchString, line) || []; - result.push(...matches.map(match => { - startLineNumber: setting.descriptionRanges[i].startLineNumber + i, - startColumn: setting.descriptionRanges[i].startColumn + match.start, - endLineNumber: setting.descriptionRanges[i].startLineNumber + i, - endColumn: setting.descriptionRanges[i].startColumn + match.end - })); - + for (const word of words) { + const ranges = from.get(word); + if (ranges) { + result.push(...ranges); + } else if (others.every(o => !o.has(word))) { + return []; + } } return result; } - private _findMatchesInSettingKey(searchString: string, setting: ISetting): IRange[] { - const matches = or(matchesPrefix, matchesContiguousSubString, matchesWords)(searchString, setting.key) || []; - return matches.map(match => { + private toKeyRange(setting: ISetting, match: IMatch): IRange { + return { startLineNumber: setting.keyRange.startLineNumber, startColumn: setting.keyRange.startColumn + match.start, endLineNumber: setting.keyRange.startLineNumber, endColumn: setting.keyRange.startColumn + match.end - }); + }; + } + + private toDescriptionRange(setting: ISetting, match: IMatch, lineIndex: number): IRange { + return { + startLineNumber: setting.descriptionRanges[lineIndex].startLineNumber + lineIndex, + startColumn: setting.descriptionRanges[lineIndex].startColumn + match.start, + endLineNumber: setting.descriptionRanges[lineIndex].startLineNumber + lineIndex, + endColumn: setting.descriptionRanges[lineIndex].startColumn + match.end + }; + } + + private toValueRange(setting: ISetting, match: IMatch): IRange { + return { + startLineNumber: setting.valueRange.startLineNumber, + startColumn: setting.valueRange.startColumn + match.start + 1, + endLineNumber: setting.valueRange.startLineNumber, + endColumn: setting.valueRange.startColumn + match.end + 1 + }; } public abstract settingsGroups: ISettingsGroup[]; From 7b663a459a0906099771fd57ab504d19a9e3fb29 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 3 Jan 2017 18:42:44 +0100 Subject: [PATCH 048/131] Preserve zen mode on reload fixes #18016 --- src/vs/workbench/electron-browser/workbench.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 17c34c12ed0..ff5e2429ae5 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -133,6 +133,7 @@ export class Workbench implements IPartService { private static sidebarHiddenSettingKey = 'workbench.sidebar.hidden'; private static sidebarRestoreSettingKey = 'workbench.sidebar.restore'; private static panelHiddenSettingKey = 'workbench.panel.hidden'; + private static zenModeActiveSettingKey = 'workbench.zenmode.active'; private static sidebarPositionConfigurationKey = 'workbench.sideBar.location'; private static statusbarVisibleConfigurationKey = 'workbench.statusBar.visible'; @@ -321,6 +322,10 @@ export class Workbench implements IPartService { }); })); + if (this.storageService.getBoolean(Workbench.zenModeActiveSettingKey, StorageScope.WORKSPACE, false)) { + this.toggleZenMode(true); + } + // Flag workbench as created once done const workbenchDone = (error?: Error) => { this.workbenchCreated = true; @@ -804,6 +809,8 @@ export class Workbench implements IPartService { if (reason === ShutdownReason.RELOAD) { this.storageService.store(Workbench.sidebarRestoreSettingKey, 'true', StorageScope.WORKSPACE); } + // Preserve zen mode only on reload. Real quit gets out of zen mode so novice users do not get stuck in zen mode. + this.storageService.store(Workbench.zenModeActiveSettingKey, reason === ShutdownReason.RELOAD && this.zenMode.active, StorageScope.WORKSPACE); // Pass shutdown on to each participant this.toShutdown.forEach(s => s.shutdown()); @@ -1055,7 +1062,7 @@ export class Workbench implements IPartService { return Identifiers.WORKBENCH_CONTAINER; } - public toggleZenMode(): void { + public toggleZenMode(skipLayout?: boolean): void { this.zenMode.active = !this.zenMode.active; // Check if zen mode transitioned to full screen and if now we are out of zen mode -> we need to go out of full screen let toggleFullScreen = false; @@ -1093,7 +1100,9 @@ export class Workbench implements IPartService { } this.inZenMode.set(this.zenMode.active); - this.layout(); + if (!skipLayout) { + this.layout(); + } if (toggleFullScreen) { this.windowService.toggleFullScreen().done(undefined, errors.onUnexpectedError); } From b9a362a18523cfc58dcb22b33a63004a8aaf0c83 Mon Sep 17 00:00:00 2001 From: Josh Peng Date: Tue, 3 Jan 2017 10:03:16 -0800 Subject: [PATCH 049/131] Improve Markdown code block tokens (#17591) * Improve Markdown code block tokens Code blocks without a language weren't tokenized. Code blocks didn't have their ending ``` punctuation tokenized. Code blocks used to only have one token. Now each block has the following tokens available for syntax highlighters: - Starting and ending ``` punctuations - Code block's language setting - Code snippet * Variable whitespace for MD code block ``` token Allow for variable amount of whitespacing before ``` code blocks * Reorder raw blocks Raw blocks were preventing tokenizing as languaged blocks. Putting them on bottom resolves this. * Fix MD block detection when following paragraph Used to require a new line inbetween ``` code blocks and preceding paragraph text. * Prevent broken language grammar leaks in MD fences Prevents leaks in MD code fences while also capturing the closing fence punctuations. * Update Markdown tokenizer test file --- .../markdown/syntaxes/markdown.tmLanguage | 1843 +++++++++++++++-- .../test/colorize-results/test_md.json | 82 +- 2 files changed, 1638 insertions(+), 287 deletions(-) diff --git a/extensions/markdown/syntaxes/markdown.tmLanguage b/extensions/markdown/syntaxes/markdown.tmLanguage index b0982a11087..031589c965a 100644 --- a/extensions/markdown/syntaxes/markdown.tmLanguage +++ b/extensions/markdown/syntaxes/markdown.tmLanguage @@ -48,10 +48,10 @@ include - #raw_block - - - include + + + + #fenced_code_block_css @@ -202,6 +202,14 @@ include #fenced_code_block_csharp + + include + #fenced_code_block_unknown + + + include + #raw_block + include #link-def @@ -538,156 +546,480 @@ (^|\G)(?!\s*$|#|[ ]{0,3}((([*_][ ]{0,2}\2){2,}[ \t]*$\n?)|([*+-]([ ]{1,3}|\t)))|\s*\[.+?\]:|>) - raw_block - - begin - (^|\G)([ ]{4}|\t) - name - markup.raw.block.markdown - while - (^|\G)([ ]{4}|\t) - + + + + + + + + + fenced_code_block_css begin - (^|\G)\s*(([`~]){3,})\s*(css|css.erb)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((css|css.erb)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.css + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.css + + fenced_code_block_basic begin - (^|\G)\s*(([`~]){3,})\s*(html|htm|shtml|xhtml|inc|tmpl|tpl)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((html|htm|shtml|xhtml|inc|tmpl|tpl)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - text.html.basic + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + text.html.basic + + fenced_code_block_ini begin - (^|\G)\s*(([`~]){3,})\s*(ini|conf)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((ini|conf)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.ini + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.ini + + fenced_code_block_java begin - (^|\G)\s*(([`~]){3,})\s*(java|bsh)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((java|bsh)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.java + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.java + + fenced_code_block_lua begin - (^|\G)\s*(([`~]){3,})\s*(lua)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((lua)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.lua + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.lua + + fenced_code_block_makefile begin - (^|\G)\s*(([`~]){3,})\s*(Makefile|makefile|GNUmakefile|OCamlMakefile)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((Makefile|makefile|GNUmakefile|OCamlMakefile)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.makefile + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.makefile + + fenced_code_block_perl begin - (^|\G)\s*(([`~]){3,})\s*(perl|pl|pm|pod|t|PL|psgi|vcl)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((perl|pl|pm|pod|t|PL|psgi|vcl)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.perl + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.perl + + fenced_code_block_r begin - (^|\G)\s*(([`~]){3,})\s*(R|r|s|S|Rprofile)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((R|r|s|S|Rprofile)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.r + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.r + + fenced_code_block_ruby begin - (^|\G)\s*(([`~]){3,})\s*(ruby|rb|rbx|rjs|Rakefile|rake|cgi|fcgi|gemspec|irbrc|Capfile|ru|prawn|Cheffile|Gemfile|Guardfile|Hobofile|Vagrantfile|Appraisals|Rantfile|Berksfile|Berksfile.lock|Thorfile|Puppetfile)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((ruby|rb|rbx|rjs|Rakefile|rake|cgi|fcgi|gemspec|irbrc|Capfile|ru|prawn|Cheffile|Gemfile|Guardfile|Hobofile|Vagrantfile|Appraisals|Rantfile|Berksfile|Berksfile.lock|Thorfile|Puppetfile)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.ruby + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.ruby + + @@ -706,477 +1038,1562 @@ scenarios, and also appears to be consistent with the approach that GitHub takes. begin - (^|\G)\s*(([`~]){3,})\s*(php|php3|php4|php5|phpt|phtml|aw|ctp)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((php|php3|php4|php5|phpt|phtml|aw|ctp)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - comment - - Left to its own devices, the PHP grammar will match HTML as a combination of operators - and constants. Therefore, HTML must take precedence over PHP in order to get proper - syntax highlighting. - - include - text.html.basic - - - include - text.html.php#language + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + comment + + + + Left to its own devices, the PHP grammar will match HTML as a combination of operators + and constants. Therefore, HTML must take precedence over PHP in order to get proper + syntax highlighting. + + include + text.html.basic + + + include + text.html.php#language + + fenced_code_block_sql begin - (^|\G)\s*(([`~]){3,})\s*(sql|ddl|dml)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((sql|ddl|dml)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.sql + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.sql + + fenced_code_block_vs_net begin - (^|\G)\s*(([`~]){3,})\s*(vb)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((vb)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.asp.vb.net + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.asp.vb.net + + fenced_code_block_xml begin - (^|\G)\s*(([`~]){3,})\s*(xml|xsd|tld|jsp|pt|cpt|dtml|rss|opml)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((xml|xsd|tld|jsp|pt|cpt|dtml|rss|opml)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - text.xml + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + text.xml + + fenced_code_block_xsl begin - (^|\G)\s*(([`~]){3,})\s*(xsl|xslt)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((xsl|xslt)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - text.xml.xsl + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + text.xml.xsl + + fenced_code_block_yaml begin - (^|\G)\s*(([`~]){3,})\s*(yaml|yml)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((yaml|yml)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.yaml + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.yaml + + fenced_code_block_dosbatch begin - (^|\G)\s*(([`~]){3,})\s*(bat|batch)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((bat|batch)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.dosbatch + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.dosbatch + + fenced_code_block_clojure begin - (^|\G)\s*(([`~]){3,})\s*(clj|cljs|clojure)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((clj|cljs|clojure)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.clojure + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.clojure + + fenced_code_block_coffee begin - (^|\G)\s*(([`~]){3,})\s*(coffee|Cakefile|coffee.erb)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((coffee|Cakefile|coffee.erb)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.coffee + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.coffee + + fenced_code_block_c begin - (^|\G)\s*(([`~]){3,})\s*(c|h)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((c|h)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.c + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.c + + fenced_code_block_diff begin - (^|\G)\s*(([`~]){3,})\s*(patch|diff|rej)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((patch|diff|rej)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.diff + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.diff + + fenced_code_block_dockerfile begin - (^|\G)\s*(([`~]){3,})\s*(dockerfile|Dockerfile)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((dockerfile|Dockerfile)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.dockerfile + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.dockerfile + + fenced_code_block_git_commit begin - (^|\G)\s*(([`~]){3,})\s*(COMMIT_EDITMSG|MERGE_MSG)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((COMMIT_EDITMSG|MERGE_MSG)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - text.git-commit + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + text.git-commit + + fenced_code_block_git_rebase begin - (^|\G)\s*(([`~]){3,})\s*(git-rebase-todo)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((git-rebase-todo)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - text.git-rebase + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + text.git-rebase + + fenced_code_block_groovy begin - (^|\G)\s*(([`~]){3,})\s*(groovy|gvy)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((groovy|gvy)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.groovy + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.groovy + + fenced_code_block_jade begin - (^|\G)\s*(([`~]){3,})\s*(jade)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((jade)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - text.jade + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + text.jade + + fenced_code_block_js begin - (^|\G)\s*(([`~]){3,})\s*(js|jsx|javascript)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((js|jsx|javascript)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.js + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.js + + fenced_code_block_js_regexp begin - (^|\G)\s*(([`~]){3,})\s*(regexp)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((regexp)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.js.regexp + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.js.regexp + + fenced_code_block_json begin - (^|\G)\s*(([`~]){3,})\s*(json|sublime-settings|sublime-menu|sublime-keymap|sublime-mousemap|sublime-theme|sublime-build|sublime-project|sublime-completions)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((json|sublime-settings|sublime-menu|sublime-keymap|sublime-mousemap|sublime-theme|sublime-build|sublime-project|sublime-completions)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.json + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.json + + fenced_code_block_less begin - (^|\G)\s*(([`~]){3,})\s*(less)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((less)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.css.less + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.css.less + + fenced_code_block_objc begin - (^|\G)\s*(([`~]){3,})\s*(objectivec|mm|objc|obj-c|m|h)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((objectivec|mm|objc|obj-c|m|h)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.objc + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.objc + + fenced_code_block_perl6 begin - (^|\G)\s*(([`~]){3,})\s*(perl6|p6|pl6|pm6|nqp)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((perl6|p6|pl6|pm6|nqp)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.perl.6 + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.perl.6 + + fenced_code_block_powershell begin - (^|\G)\s*(([`~]){3,})\s*(powershell|ps1|psm1|psd1)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((powershell|ps1|psm1|psd1)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.powershell + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.powershell + + fenced_code_block_python begin - (^|\G)\s*(([`~]){3,})\s*(python|py|py3|rpy|pyw|cpy|SConstruct|Sconstruct|sconstruct|SConscript|gyp|gypi)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((python|py|py3|rpy|pyw|cpy|SConstruct|Sconstruct|sconstruct|SConscript|gyp|gypi)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.python + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.python + + fenced_code_block_regexp_python begin - (^|\G)\s*(([`~]){3,})\s*(re)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((re)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.regexp.python + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.regexp.python + + fenced_code_block_shell begin - (^|\G)\s*(([`~]){3,})\s*(shell|sh|bash|zsh|bashrc|bash_profile|bash_login|profile|bash_logout|.textmate_init)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((shell|sh|bash|zsh|bashrc|bash_profile|bash_login|profile|bash_logout|.textmate_init)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.shell + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.shell + + fenced_code_block_ts begin - (^|\G)\s*(([`~]){3,})\s*(typescript|ts)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((typescript|ts)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.ts + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.ts + + fenced_code_block_tsx begin - (^|\G)\s*(([`~]){3,})\s*(tsx)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((tsx)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.tsx + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.tsx + + fenced_code_block_csharp begin - (^|\G)\s*(([`~]){3,})\s*(cs|csharp|c#)(\s+.*)?$ + (^|\G)(\s*)([`~]{3,})\s*((cs|csharp|c#)(\s+.*)?$) name markup.fenced_code.block.markdown - while - (^|\G)(?!\s*\2\3*\s*$) + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 5 + + name + fenced_code.block.language + + 6 + + name + fenced_code.block.language.attributes + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + patterns - include - source.cs + begin + (^|\G)(\s*)(.*) + while + (^|\G)(?!\s*([`~]{3,})\s*$) + patterns + + + include + source.cs + + + fenced_code_block_unknown + + name + markup.fenced_code.block.markdown + begin + (^|\G)(\s*)([`~]{3,})\s*(.*)?$ + end + (^|\G)(\2|\s{0,3})(\3)\s*$ + beginCaptures + + 3 + + name + punctuation.definition.markdown + + 4 + + name + fenced_code.block.language + + + endCaptures + + 3 + + name + punctuation.definition.markdown + + + + raw_block + + begin + (^|\G)([ ]{4}|\t) + name + markup.raw.block.markdown + while + (^|\G)([ ]{4}|\t) + separator match diff --git a/extensions/markdown/test/colorize-results/test_md.json b/extensions/markdown/test/colorize-results/test_md.json index be3e30c55c0..ffb4d5d66e8 100644 --- a/extensions/markdown/test/colorize-results/test_md.json +++ b/extensions/markdown/test/colorize-results/test_md.json @@ -1827,7 +1827,7 @@ }, { "c": "~~~", - "t": "markdown.meta.paragraph", + "t": "block.definition.fenced_code.markdown.markup.punctuation", "r": { "dark_plus": ".vs-dark .token rgb(212, 212, 212)", "light_plus": ".vs .token rgb(0, 0, 0)", @@ -1838,7 +1838,7 @@ }, { "c": "// Markdown extra adds un-indented code blocks too", - "t": "markdown.meta.paragraph", + "t": "block.fenced_code.markdown.markup", "r": { "dark_plus": ".vs-dark .token rgb(212, 212, 212)", "light_plus": ".vs .token rgb(0, 0, 0)", @@ -1848,74 +1848,8 @@ } }, { - "c": "if (this", - "t": "markdown.meta.paragraph", - "r": { - "dark_plus": ".vs-dark .token rgb(212, 212, 212)", - "light_plus": ".vs .token rgb(0, 0, 0)", - "dark_vs": ".vs-dark .token rgb(212, 212, 212)", - "light_vs": ".vs .token rgb(0, 0, 0)", - "hc_black": ".hc-black .token rgb(255, 255, 255)" - } - }, - { - "c": "_", - "t": "definition.italic.markdown.markup.meta.paragraph.punctuation", - "r": { - "dark_plus": ".vs-dark.vscode-theme-defaults-themes-dark_plus-json .token.markup.italic rgb(212, 212, 212)", - "light_plus": ".vs.vscode-theme-defaults-themes-light_plus-json .token.markup.italic rgb(0, 0, 0)", - "dark_vs": ".vs-dark.vscode-theme-defaults-themes-dark_vs-json .token.markup.italic rgb(212, 212, 212)", - "light_vs": ".vs.vscode-theme-defaults-themes-light_vs-json .token.markup.italic rgb(0, 0, 0)", - "hc_black": ".hc-black.vscode-theme-defaults-themes-hc_black-json .token.markup.italic rgb(255, 255, 255)" - } - }, - { - "c": "is", - "t": "italic.markdown.markup.meta.paragraph", - "r": { - "dark_plus": ".vs-dark.vscode-theme-defaults-themes-dark_plus-json .token.markup.italic rgb(212, 212, 212)", - "light_plus": ".vs.vscode-theme-defaults-themes-light_plus-json .token.markup.italic rgb(0, 0, 0)", - "dark_vs": ".vs-dark.vscode-theme-defaults-themes-dark_vs-json .token.markup.italic rgb(212, 212, 212)", - "light_vs": ".vs.vscode-theme-defaults-themes-light_vs-json .token.markup.italic rgb(0, 0, 0)", - "hc_black": ".hc-black.vscode-theme-defaults-themes-hc_black-json .token.markup.italic rgb(255, 255, 255)" - } - }, - { - "c": "_", - "t": "definition.italic.markdown.markup.meta.paragraph.punctuation", - "r": { - "dark_plus": ".vs-dark.vscode-theme-defaults-themes-dark_plus-json .token.markup.italic rgb(212, 212, 212)", - "light_plus": ".vs.vscode-theme-defaults-themes-light_plus-json .token.markup.italic rgb(0, 0, 0)", - "dark_vs": ".vs-dark.vscode-theme-defaults-themes-dark_vs-json .token.markup.italic rgb(212, 212, 212)", - "light_vs": ".vs.vscode-theme-defaults-themes-light_vs-json .token.markup.italic rgb(0, 0, 0)", - "hc_black": ".hc-black.vscode-theme-defaults-themes-hc_black-json .token.markup.italic rgb(255, 255, 255)" - } - }, - { - "c": "more_code == true ", - "t": "markdown.meta.paragraph", - "r": { - "dark_plus": ".vs-dark .token rgb(212, 212, 212)", - "light_plus": ".vs .token rgb(0, 0, 0)", - "dark_vs": ".vs-dark .token rgb(212, 212, 212)", - "light_vs": ".vs .token rgb(0, 0, 0)", - "hc_black": ".hc-black .token rgb(255, 255, 255)" - } - }, - { - "c": "&&", - "t": "markdown.meta.other.paragraph.valid-ampersand", - "r": { - "dark_plus": ".vs-dark .token rgb(212, 212, 212)", - "light_plus": ".vs .token rgb(0, 0, 0)", - "dark_vs": ".vs-dark .token rgb(212, 212, 212)", - "light_vs": ".vs .token rgb(0, 0, 0)", - "hc_black": ".hc-black .token rgb(255, 255, 255)" - } - }, - { - "c": " !indented) {", - "t": "markdown.meta.paragraph", + "c": "if (this_is_more_code == true && !indented) {", + "t": "block.fenced_code.markdown.markup", "r": { "dark_plus": ".vs-dark .token rgb(212, 212, 212)", "light_plus": ".vs .token rgb(0, 0, 0)", @@ -1926,7 +1860,7 @@ }, { "c": " // tild wrapped code blocks, also not indented", - "t": "markdown.meta.paragraph", + "t": "block.fenced_code.markdown.markup", "r": { "dark_plus": ".vs-dark .token rgb(212, 212, 212)", "light_plus": ".vs .token rgb(0, 0, 0)", @@ -1937,7 +1871,7 @@ }, { "c": "}", - "t": "markdown.meta.paragraph", + "t": "block.fenced_code.markdown.markup", "r": { "dark_plus": ".vs-dark .token rgb(212, 212, 212)", "light_plus": ".vs .token rgb(0, 0, 0)", @@ -1948,7 +1882,7 @@ }, { "c": "~~~", - "t": "markdown.meta.paragraph", + "t": "block.definition.fenced_code.markdown.markup.punctuation", "r": { "dark_plus": ".vs-dark .token rgb(212, 212, 212)", "light_plus": ".vs .token rgb(0, 0, 0)", @@ -2595,4 +2529,4 @@ "hc_black": ".hc-black .token rgb(255, 255, 255)" } } -] \ No newline at end of file +] From 31e1aebf30e5196ab3707824c905b0d7832b5117 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 3 Jan 2017 11:15:05 -0800 Subject: [PATCH 050/131] Fix markdown colorization tests --- .../test/colorize-results/test_md.json | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/extensions/markdown/test/colorize-results/test_md.json b/extensions/markdown/test/colorize-results/test_md.json index ffb4d5d66e8..e02382cc8d3 100644 --- a/extensions/markdown/test/colorize-results/test_md.json +++ b/extensions/markdown/test/colorize-results/test_md.json @@ -1584,8 +1584,8 @@ } }, { - "c": "````application/json", - "t": "markdown.meta.paragraph", + "c": "````", + "t": "block.definition.fenced_code.markdown.markup.punctuation", "r": { "dark_plus": ".vs-dark .token rgb(212, 212, 212)", "light_plus": ".vs .token rgb(0, 0, 0)", @@ -1594,9 +1594,20 @@ "hc_black": ".hc-black .token rgb(255, 255, 255)" } }, + { + "c": "application/json", + "r": { + "dark_plus": ".vs-dark .token rgb(212, 212, 212)", + "dark_vs": ".vs-dark .token rgb(212, 212, 212)", + "hc_black": ".hc-black .token rgb(255, 255, 255)", + "light_plus": ".vs .token rgb(0, 0, 0)", + "light_vs": ".vs .token rgb(0, 0, 0)" + }, + "t": "block.fenced_code.language.markdown.markup" + }, { "c": " { value: [\"or with a mime type\"] }", - "t": "markdown.meta.paragraph", + "t": "block.fenced_code.markdown.markup", "r": { "dark_plus": ".vs-dark .token rgb(212, 212, 212)", "light_plus": ".vs .token rgb(0, 0, 0)", @@ -1607,7 +1618,7 @@ }, { "c": "````", - "t": "markdown.meta.paragraph", + "t": "block.definition.fenced_code.markdown.markup.punctuation", "r": { "dark_plus": ".vs-dark .token rgb(212, 212, 212)", "light_plus": ".vs .token rgb(0, 0, 0)", From 43a3373b6ab926c1534eaa2f72097e71a3c29f5d Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 3 Jan 2017 20:48:07 +0100 Subject: [PATCH 051/131] find widget: 'no results' ui polish --- src/vs/editor/contrib/find/browser/findWidget.css | 8 +++++--- src/vs/editor/contrib/find/browser/findWidget.ts | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/find/browser/findWidget.css b/src/vs/editor/contrib/find/browser/findWidget.css index 38bba6c2f09..91f4f37bd69 100644 --- a/src/vs/editor/contrib/find/browser/findWidget.css +++ b/src/vs/editor/contrib/find/browser/findWidget.css @@ -105,10 +105,12 @@ } .monaco-editor .find-widget.no-results .matchesCount { - background-color: rgba(255,0,0,0.5); + color: #A1260D; } -.monaco-editor.vs-dark .find-widget.no-results .matchesCount { - background-color: rgba(255,0,0,0.3); + +.monaco-editor.vs-dark .find-widget.no-results .matchesCount, +.monaco-editor.hc-black .find-widget.no-results .matchesCount { + color: #F48771 } .monaco-editor .find-widget .matchesCount { diff --git a/src/vs/editor/contrib/find/browser/findWidget.ts b/src/vs/editor/contrib/find/browser/findWidget.ts index bc352658822..e4391583d78 100644 --- a/src/vs/editor/contrib/find/browser/findWidget.ts +++ b/src/vs/editor/contrib/find/browser/findWidget.ts @@ -43,7 +43,7 @@ const NLS_REPLACE_ALL_BTN_LABEL = nls.localize('label.replaceAllButton', "Replac const NLS_TOGGLE_REPLACE_MODE_BTN_LABEL = nls.localize('label.toggleReplaceButton', "Toggle Replace mode"); const NLS_MATCHES_COUNT_LIMIT_TITLE = nls.localize('title.matchesCountLimit', "Only the first 999 results are highlighted, but all find operations work on the entire text."); const NLS_MATCHES_LOCATION = nls.localize('label.matchesLocation', "{0} of {1}"); -const NLS_NO_RESULTS = nls.localize('label.noResults', "No results"); +const NLS_NO_RESULTS = nls.localize('label.noResults', "No Results"); let MAX_MATCHES_COUNT_WIDTH = 69; const WIDGET_FIXED_WIDTH = 411 - 69; From 07dfb5119e2bf0ba43b67551b2a22ed43d62ef3b Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 3 Jan 2017 21:03:29 +0100 Subject: [PATCH 052/131] vscode.startDebug pass selected configuration name as default value --- .../parts/debug/electron-browser/debug.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts index c04bab73036..0f9d61b4827 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts @@ -133,7 +133,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0), handler(accessor: ServicesAccessor, configurationOrName: any) { const debugService = accessor.get(IDebugService); - return debugService.createProcess(configurationOrName); + return debugService.createProcess(configurationOrName || debugService.getViewModel().selectedConfigurationName); }, when: CONTEXT_NOT_IN_DEBUG_MODE, primary: undefined From c62d20228b84b3aa00269d63f44cab41b357700b Mon Sep 17 00:00:00 2001 From: roblou Date: Tue, 3 Jan 2017 13:48:23 -0800 Subject: [PATCH 053/131] Show search results when navigating with the arrow keys - partially address Microsoft/vscode#17324 but also will add a keyboard shortcut --- .../parts/search/browser/searchResultsView.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index 10b3a248a12..f08db80855d 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -282,7 +282,18 @@ export class SearchController extends DefaultController { this.viewlet.moveFocusFromResults(); return true; } - return super.onUp(tree, event); + + const result = super.onUp(tree, event); + let focus = tree.getFocus(); + this.selectOnScroll(tree, focus, event); + return result; + } + + protected onDown(tree: ITree, event: IKeyboardEvent): boolean { + const result = super.onDown(tree, event); + let focus = tree.getFocus(); + this.selectOnScroll(tree, focus, event); + return result; } protected onSpace(tree: ITree, event: IKeyboardEvent): boolean { @@ -292,6 +303,14 @@ export class SearchController extends DefaultController { } super.onSpace(tree, event); } + + private selectOnScroll(tree: ITree, focus: any, event: IKeyboardEvent): void { + if (focus instanceof Match) { + this.onEnter(tree, event); + } else { + tree.setSelection([focus]); + } + } } export class SearchFilter implements IFilter { From 68274bfd8f3d724874b8aead1c0f5aa663ad156c Mon Sep 17 00:00:00 2001 From: roblou Date: Tue, 3 Jan 2017 15:57:28 -0800 Subject: [PATCH 054/131] Microsoft/vscode#17324 - Implement F4 to navigate results --- .../search/browser/search.contribution.ts | 3 ++ .../parts/search/browser/searchActions.ts | 24 +++++++++ .../parts/search/browser/searchViewlet.ts | 53 ++++++++++++++++++- .../parts/search/common/constants.ts | 3 ++ 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/search/browser/search.contribution.ts b/src/vs/workbench/parts/search/browser/search.contribution.ts index 49de2d67ff0..24e2a7b4c05 100644 --- a/src/vs/workbench/parts/search/browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/browser/search.contribution.ts @@ -126,6 +126,9 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FocusAct registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FindInFilesAction, Constants.FindInFilesActionId, nls.localize('findInFiles', "Find in Files"), { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }, Constants.SearchInputBoxFocussedKey.toNegated()), 'Find in Files'); +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FocusNextSearchResultAction, Constants.FocusNextSearchResultActionId, 'Focus next result', { primary: KeyCode.F4 }), 'Focus next result'); +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FocusPreviousSearchResultAction, Constants.FocusPreviousSearchResultActionId, 'Focus previous result', { primary: KeyMod.Shift | KeyCode.F4 }), 'Focus next result'); + registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ReplaceInFilesAction, searchActions.ReplaceInFilesAction.ID, searchActions.ReplaceInFilesAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_H }), 'Replace in Files'); registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.CloseReplaceAction, Constants.CloseReplaceWidgetActionId, '', { primary: KeyCode.Escape }, ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.ReplaceInputBoxFocussedKey)), ''); diff --git a/src/vs/workbench/parts/search/browser/searchActions.ts b/src/vs/workbench/parts/search/browser/searchActions.ts index 66fed1dbee6..26c1bc05d25 100644 --- a/src/vs/workbench/parts/search/browser/searchActions.ts +++ b/src/vs/workbench/parts/search/browser/searchActions.ts @@ -266,6 +266,30 @@ export class ClearSearchResultsAction extends Action { } } +export class FocusNextSearchResultAction extends Action { + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + super('focusNextSearchResult', nls.localize('FocusNextSearchResult.label', "Focus next search result")); + } + + public run(): TPromise { + return this.viewletService.openViewlet(Constants.VIEWLET_ID).then((searchViewlet: SearchViewlet) => { + searchViewlet.selectNextResult(); + }); + } +} + +export class FocusPreviousSearchResultAction extends Action { + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + super('focusPreviousSearchResult', nls.localize('FocusPreviousSearchResult.label', "Focus previous search result")); + } + + public run(): TPromise { + return this.viewletService.openViewlet(Constants.VIEWLET_ID).then((searchViewlet: SearchViewlet) => { + searchViewlet.selectPreviousResult(); + }); + } +} + export abstract class AbstractSearchAndReplaceAction extends Action { /** diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts index 255eae24ba1..fe752040bfe 100644 --- a/src/vs/workbench/parts/search/browser/searchViewlet.ts +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -410,7 +410,7 @@ export class SearchViewlet extends Viewlet { } let sideBySide = (originalEvent && (originalEvent.ctrlKey || originalEvent.metaKey)); - let focusEditor = (keyboard && (originalEvent).keyCode === KeyCode.Enter) || doubleClick; + let focusEditor = (keyboard && (originalEvent).keyCode === KeyCode.Enter) || doubleClick || event.payload.focusEditor; if (element instanceof Match) { let selectedMatch: Match = element; @@ -449,6 +449,57 @@ export class SearchViewlet extends Viewlet { } } + public selectNextResult(): void { + const eventPayload = { focusEditor: true }; + + this.tree.selectNext(undefined, undefined, eventPayload); + let [selected]: FileMatchOrMatch[] = this.tree.getSelection(); + if (!selected) { + return; + } + + // Expand and go past FileMatch nodes + if (!(selected instanceof Match)) { + if (!this.tree.isExpanded(selected)) { + this.tree.expand(selected); + } + + // Select the FileMatch's first child + this.tree.selectNext(undefined, undefined, eventPayload); + } + + // Reveal the newly selected element + [selected] = this.tree.getSelection(); + this.tree.reveal(selected); + } + + public selectPreviousResult(): void { + const eventPayload = { focusEditor: true }; + + // previous with no current selection? + this.tree.selectPrevious(undefined, undefined, eventPayload); + let [selected]: FileMatchOrMatch[] = this.tree.getSelection(); + if (!selected) { + return; + } + + // Expand and go past FileMatch nodes + if (!(selected instanceof Match)) { + this.tree.selectPrevious(undefined, undefined, eventPayload); + [selected] = this.tree.getSelection(); + + if (!(selected instanceof Match)) { + this.tree.selectNext(undefined, undefined, eventPayload); + this.tree.expand(selected); + this.tree.selectPrevious(undefined, undefined, eventPayload); + } + } + + // Reveal the newly selected element + [selected] = this.tree.getSelection(); + this.tree.reveal(selected); + } + public setVisible(visible: boolean): TPromise { let promise: TPromise; this.viewletVisible.set(visible); diff --git a/src/vs/workbench/parts/search/common/constants.ts b/src/vs/workbench/parts/search/common/constants.ts index 8b3a46f966d..090bea80e1c 100644 --- a/src/vs/workbench/parts/search/common/constants.ts +++ b/src/vs/workbench/parts/search/common/constants.ts @@ -9,6 +9,9 @@ export const VIEWLET_ID = 'workbench.view.search'; export const FindInFilesActionId = 'workbench.action.findInFiles'; export const FocusActiveEditorActionId = 'search.action.focusActiveEditor'; +export const FocusNextSearchResultActionId = 'search.action.focusNextSearchResult'; +export const FocusPreviousSearchResultActionId = 'search.action.focusPreviousSearchResult'; + export const ToggleCaseSensitiveActionId = 'toggleSearchCaseSensitive'; export const ToggleWholeWordActionId = 'toggleSearchWholeWord'; export const ToggleRegexActionId = 'toggleSearchRegex'; From e9f15e6495cbe91e30186410b9c5885c12da22f0 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 2 Jan 2017 21:57:21 -0400 Subject: [PATCH 055/131] [html] indentation after scripts broken --- extensions/html/server/src/htmlServerMain.ts | 19 +++---- .../html/server/src/modes/formatting.ts | 55 +++++++++++++++++++ .../html/server/src/test/formatting.test.ts | 33 +++++------ extensions/html/server/src/utils/edits.ts | 34 ++++++++++++ 4 files changed, 110 insertions(+), 31 deletions(-) create mode 100644 extensions/html/server/src/modes/formatting.ts create mode 100644 extensions/html/server/src/utils/edits.ts diff --git a/extensions/html/server/src/htmlServerMain.ts b/extensions/html/server/src/htmlServerMain.ts index 4d66f8e4303..14e1b342bc1 100644 --- a/extensions/html/server/src/htmlServerMain.ts +++ b/extensions/html/server/src/htmlServerMain.ts @@ -6,9 +6,11 @@ import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, RequestType } from 'vscode-languageserver'; import { DocumentContext } from 'vscode-html-languageservice'; -import { TextDocument, Diagnostic, DocumentLink, Range, TextEdit, SymbolInformation } from 'vscode-languageserver-types'; +import { TextDocument, Diagnostic, DocumentLink, Range, SymbolInformation } from 'vscode-languageserver-types'; import { getLanguageModes, LanguageModes } from './modes/languageModes'; +import { format } from './modes/formatting'; + import * as url from 'url'; import * as path from 'path'; import uri from 'vscode-uri'; @@ -201,18 +203,11 @@ connection.onSignatureHelp(signatureHelpParms => { connection.onDocumentRangeFormatting(formatParams => { let document = documents.get(formatParams.textDocument.uri); - let ranges = languageModes.getModesInRange(document, formatParams.range); - let result: TextEdit[] = []; + let unformattedTags: string = settings && settings.html && settings.html.format && settings.html.format.unformatted || ''; - let enabledModes = { css: !unformattedTags.match(/\bstyle\b/), javascript: !unformattedTags.match(/\bscript\b/), html: true }; - ranges.forEach(r => { - let mode = r.mode; - if (mode && mode.format && enabledModes[mode.getId()] && !r.attributeValue) { - let edits = mode.format(document, r, formatParams.options); - pushAll(result, edits); - } - }); - return result; + let enabledModes = { css: !unformattedTags.match(/\bstyle\b/), javascript: !unformattedTags.match(/\bscript\b/) }; + + return format(languageModes, document, formatParams.range, formatParams.options, enabledModes); }); connection.onDocumentLinks(documentLinkParam => { diff --git a/extensions/html/server/src/modes/formatting.ts b/extensions/html/server/src/modes/formatting.ts new file mode 100644 index 00000000000..6213ad71b92 --- /dev/null +++ b/extensions/html/server/src/modes/formatting.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { applyEdits } from '../utils/edits'; +import { TextDocument, Range, TextEdit, FormattingOptions } from 'vscode-languageserver-types'; +import { LanguageModes } from './languageModes'; + +export function format(languageModes: LanguageModes, document: TextDocument, formatRange: Range, formattingOptions: FormattingOptions, enabledModes: { [mode: string]: boolean }) { + // run the html formatter on the full range and pass the result content to the embedded formatters. + // from the final content create a single edit + // advantages of this approach are + // - correct indents in the html document + // - correct initial indent for embedded formatters + // - no worrying of overlapping edits + + // perform a html format and apply changes to a new document + let htmlMode = languageModes.getMode('html'); + let htmlEdits = htmlMode.format(document, formatRange, formattingOptions); + let htmlFormattedContent = applyEdits(document, htmlEdits); + let newDocument = TextDocument.create(document.uri + '.tmp', document.languageId, document.version, htmlFormattedContent); + try { + // run embedded formatters on html formatted content: - formatters see correct initial indent + let afterFormatRangeLength = document.getText().length - document.offsetAt(formatRange.end); // length of unchanged content after replace range + let newFormatRange = Range.create(formatRange.start, newDocument.positionAt(htmlFormattedContent.length - afterFormatRangeLength)); + let embeddedRanges = languageModes.getModesInRange(newDocument, newFormatRange); + + let embeddedEdits: TextEdit[] = []; + + for (let r of embeddedRanges) { + let mode = r.mode; + if (mode && mode.format && enabledModes[mode.getId()] && !r.attributeValue) { + let edits = mode.format(newDocument, r, formattingOptions); + for (let edit of edits) { + embeddedEdits.push(edit); + } + } + }; + + if (embeddedEdits.length === 0) { + return htmlEdits; + } + + // apply all embedded format edits and create a single edit for all changes + let resultContent = applyEdits(newDocument, embeddedEdits); + let resultReplaceText = resultContent.substring(document.offsetAt(formatRange.start), resultContent.length - afterFormatRangeLength); + + return [TextEdit.replace(formatRange, resultReplaceText)]; + } finally { + languageModes.onDocumentRemoved(newDocument); + } + +} \ No newline at end of file diff --git a/extensions/html/server/src/test/formatting.test.ts b/extensions/html/server/src/test/formatting.test.ts index 84ea218e779..be6a2a5db69 100644 --- a/extensions/html/server/src/test/formatting.test.ts +++ b/extensions/html/server/src/test/formatting.test.ts @@ -8,6 +8,8 @@ import * as assert from 'assert'; import { getLanguageModes } from '../modes/languageModes'; import { TextDocument, Range, TextEdit, FormattingOptions } from 'vscode-languageserver-types'; +import { format } from '../modes/formatting'; + suite('HTML Embedded Formatting', () => { function assertFormat(value: string, expected: string, options?: any): void { @@ -31,15 +33,8 @@ suite('HTML Embedded Formatting', () => { let range = Range.create(document.positionAt(rangeStartOffset), document.positionAt(rangeEndOffset)); let formatOptions = FormattingOptions.create(2, true); - let ranges = languageModes.getModesInRange(document, range); - let result: TextEdit[] = []; - ranges.forEach(r => { - let mode = r.mode; - if (mode && mode.format) { - let edits = mode.format(document, r, formatOptions); - pushAll(result, edits); - } - }); + let result = format(languageModes, document, range, formatOptions, { css: true, javascript: true }); + let actual = applyEdits(document, result); assert.equal(actual, expected); } @@ -52,38 +47,38 @@ suite('HTML Embedded Formatting', () => { test('HTML & Scripts', function (): any { assertFormat('', '\n\n\n \n\n\n'); - assertFormat('', '\n\n\n \n\n\n'); - assertFormat('', '\n\n\n \n\n\n'); - assertFormat('\n ', '\n\n\n \n\n\n'); - assertFormat('\n ', '\n\n\n \n\n\n'); + assertFormat('', '\n\n\n \n\n\n'); + assertFormat('', '\n\n\n \n\n\n'); + assertFormat('\n ', '\n\n\n \n\n\n'); + assertFormat('\n ', '\n\n\n \n\n\n'); - assertFormat('\n ||', '\n '); + assertFormat('\n ||', '\n '); assertFormat('\n ', '\n '); }); test('Script end tag', function (): any { - assertFormat('\n\n ', '\n\n\n \n\n\n'); + assertFormat('\n\n ', '\n\n\n \n\n\n'); }); test('HTML & Multiple Scripts', function (): any { - assertFormat('\n', '\n\n\n \n\n\n\n'); + assertFormat('\n', '\n\n\n \n \n\n\n'); }); test('HTML & Styles', function (): any { - assertFormat('\n', '\n\n\n \n\n\n'); + assertFormat('\n', '\n\n\n \n\n\n'); }); test('EndWithNewline', function (): any { let options = { html: { format: { - endWithNewline : true + endWithNewline: true } } }; assertFormat('

Hello

', '\n\n\n

Hello

\n\n\n\n', options); assertFormat('|

Hello

|', '\n

Hello

\n', options); - assertFormat('', '\n\n\n \n\n\n\n', options); + assertFormat('', '\n\n\n \n\n\n\n', options); }); }); diff --git a/extensions/html/server/src/utils/edits.ts b/extensions/html/server/src/utils/edits.ts new file mode 100644 index 00000000000..5983d9014fb --- /dev/null +++ b/extensions/html/server/src/utils/edits.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TextDocument, TextEdit, Position } from 'vscode-languageserver-types'; + +export function applyEdits(document: TextDocument, edits: TextEdit[]): string { + let text = document.getText(); + let sortedEdits = edits.sort((a, b) => { + let startDiff = comparePositions(a.range.start, b.range.start); + if (startDiff === 0) { + return comparePositions(a.range.end, b.range.end); + } + return startDiff; + }); + let lastOffset = text.length; + sortedEdits.forEach(e => { + let startOffset = document.offsetAt(e.range.start); + let endOffset = document.offsetAt(e.range.end); + text = text.substring(0, startOffset) + e.newText + text.substring(endOffset, text.length); + lastOffset = startOffset; + }); + return text; +} + +function comparePositions(p1: Position, p2: Position) { + let diff = p2.line - p1.line; + if (diff === 0) { + return p2.character - p1.character; + } + return diff; +} \ No newline at end of file From 420de32d3c94fea1b20a74831453617ca9bfb0fc Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 2 Jan 2017 21:57:58 -0400 Subject: [PATCH 056/131] [html] can not disable angular proposals anymore --- extensions/html/server/src/modes/htmlMode.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/html/server/src/modes/htmlMode.ts b/extensions/html/server/src/modes/htmlMode.ts index ecee841da41..55778a8a7b8 100644 --- a/extensions/html/server/src/modes/htmlMode.ts +++ b/extensions/html/server/src/modes/htmlMode.ts @@ -20,7 +20,7 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService): LanguageM settings = options && options.html; }, doComplete(document: TextDocument, position: Position) { - let options = settings && settings.html && settings.html.suggest; + let options = settings && settings.suggest; return htmlLanguageService.doComplete(document, position, htmlDocuments.get(document), options); }, doHover(document: TextDocument, position: Position) { From 939f0876c98f93ab8c0a91f602321f64b85f5a0f Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 2 Jan 2017 22:20:03 -0400 Subject: [PATCH 057/131] [html] setting to disable script & style validation --- extensions/html/package.json | 10 ++++++++++ extensions/html/package.nls.json | 4 +++- extensions/html/server/src/htmlServerMain.ts | 11 ++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/extensions/html/package.json b/extensions/html/package.json index 3e60a20edab..b7d34877f26 100644 --- a/extensions/html/package.json +++ b/extensions/html/package.json @@ -141,6 +141,16 @@ "default": true, "description": "%html.suggest.html5.desc%" }, + "html.validate.scripts": { + "type": "boolean", + "default": true, + "description": "%html.validate.scripts%" + }, + "html.validate.styles": { + "type": "boolean", + "default": true, + "description": "%html.validate.styles%" + }, "html.trace.server": { "type": "string", "enum": [ diff --git a/extensions/html/package.nls.json b/extensions/html/package.nls.json index d27f854eed5..f66e083890f 100644 --- a/extensions/html/package.nls.json +++ b/extensions/html/package.nls.json @@ -10,5 +10,7 @@ "html.format.extraLiners.desc": "List of tags, comma separated, that should have an extra newline before them. 'null' defaults to \"head, body, /html\".", "html.suggest.angular1.desc": "Configures if the built-in HTML language support suggests Angular V1 tags and properties.", "html.suggest.ionic.desc": "Configures if the built-in HTML language support suggests Ionic tags, properties and values.", - "html.suggest.html5.desc":"Configures if the built-in HTML language support suggests HTML5 tags, properties and values." + "html.suggest.html5.desc":"Configures if the built-in HTML language support suggests HTML5 tags, properties and values.", + "html.validate.scripts": "Configures if the built-in HTML language support validates embedded scripts.", + "html.validate.styles": "Configures if the built-in HTML language support validates embedded styles." } \ No newline at end of file diff --git a/extensions/html/server/src/htmlServerMain.ts b/extensions/html/server/src/htmlServerMain.ts index 14e1b342bc1..68029c1bf67 100644 --- a/extensions/html/server/src/htmlServerMain.ts +++ b/extensions/html/server/src/htmlServerMain.ts @@ -71,9 +71,18 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { }; }); +let validation = { + html: true, + css: true, + javascript: true +}; + // The settings have changed. Is send on server activation as well. connection.onDidChangeConfiguration((change) => { settings = change.settings; + let validationSettings = settings && settings.html && settings.html.validate || {}; + validation.css = validationSettings.styles !== false; + validation.javascript = validationSettings.scripts !== false; languageModes.getAllModes().forEach(m => { if (m.configure) { @@ -117,7 +126,7 @@ function triggerValidation(textDocument: TextDocument): void { function validateTextDocument(textDocument: TextDocument): void { let diagnostics: Diagnostic[] = []; languageModes.getAllModesInDocument(textDocument).forEach(mode => { - if (mode.doValidation) { + if (mode.doValidation && validation[mode.getId()]) { pushAll(diagnostics, mode.doValidation(textDocument)); } }); From ecbf70e253d42a3fcb1069011ab3663153a067ea Mon Sep 17 00:00:00 2001 From: roblou Date: Tue, 3 Jan 2017 17:29:23 -0800 Subject: [PATCH 058/131] Move id/label --- .../parts/search/browser/search.contribution.ts | 4 ++-- src/vs/workbench/parts/search/browser/searchActions.ts | 10 ++++++++-- src/vs/workbench/parts/search/browser/searchViewlet.ts | 1 - src/vs/workbench/parts/search/common/constants.ts | 2 -- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/parts/search/browser/search.contribution.ts b/src/vs/workbench/parts/search/browser/search.contribution.ts index 24e2a7b4c05..08e9598402a 100644 --- a/src/vs/workbench/parts/search/browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/browser/search.contribution.ts @@ -126,8 +126,8 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FocusAct registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FindInFilesAction, Constants.FindInFilesActionId, nls.localize('findInFiles', "Find in Files"), { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }, Constants.SearchInputBoxFocussedKey.toNegated()), 'Find in Files'); -registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FocusNextSearchResultAction, Constants.FocusNextSearchResultActionId, 'Focus next result', { primary: KeyCode.F4 }), 'Focus next result'); -registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FocusPreviousSearchResultAction, Constants.FocusPreviousSearchResultActionId, 'Focus previous result', { primary: KeyMod.Shift | KeyCode.F4 }), 'Focus next result'); +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FocusNextSearchResultAction, searchActions.FocusNextSearchResultAction.ID, searchActions.FocusNextSearchResultAction.LABEL, { primary: KeyCode.F4 }), ''); +registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.FocusPreviousSearchResultAction, searchActions.FocusPreviousSearchResultAction.ID, searchActions.FocusPreviousSearchResultAction.LABEL, { primary: KeyMod.Shift | KeyCode.F4 }), ''); registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.ReplaceInFilesAction, searchActions.ReplaceInFilesAction.ID, searchActions.ReplaceInFilesAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_H }), 'Replace in Files'); registry.registerWorkbenchAction(new SyncActionDescriptor(searchActions.CloseReplaceAction, Constants.CloseReplaceWidgetActionId, '', { primary: KeyCode.Escape }, ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.ReplaceInputBoxFocussedKey)), ''); diff --git a/src/vs/workbench/parts/search/browser/searchActions.ts b/src/vs/workbench/parts/search/browser/searchActions.ts index 26c1bc05d25..c514ad5bab9 100644 --- a/src/vs/workbench/parts/search/browser/searchActions.ts +++ b/src/vs/workbench/parts/search/browser/searchActions.ts @@ -267,8 +267,11 @@ export class ClearSearchResultsAction extends Action { } export class FocusNextSearchResultAction extends Action { + public static ID = 'search.action.focusNextSearchResult'; + public static LABEL = nls.localize('FocusNextSearchResult.label', "Focus next search result"); + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { - super('focusNextSearchResult', nls.localize('FocusNextSearchResult.label', "Focus next search result")); + super(id, label); } public run(): TPromise { @@ -279,8 +282,11 @@ export class FocusNextSearchResultAction extends Action { } export class FocusPreviousSearchResultAction extends Action { + public static ID = 'search.action.focusPreviousSearchResult'; + public static LABEL = nls.localize('FocusPreviousSearchResult.label', "Focus previous search result"); + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { - super('focusPreviousSearchResult', nls.localize('FocusPreviousSearchResult.label', "Focus previous search result")); + super(id, label); } public run(): TPromise { diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts index fe752040bfe..b0580881909 100644 --- a/src/vs/workbench/parts/search/browser/searchViewlet.ts +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -476,7 +476,6 @@ export class SearchViewlet extends Viewlet { public selectPreviousResult(): void { const eventPayload = { focusEditor: true }; - // previous with no current selection? this.tree.selectPrevious(undefined, undefined, eventPayload); let [selected]: FileMatchOrMatch[] = this.tree.getSelection(); if (!selected) { diff --git a/src/vs/workbench/parts/search/common/constants.ts b/src/vs/workbench/parts/search/common/constants.ts index 090bea80e1c..42daf7da501 100644 --- a/src/vs/workbench/parts/search/common/constants.ts +++ b/src/vs/workbench/parts/search/common/constants.ts @@ -9,8 +9,6 @@ export const VIEWLET_ID = 'workbench.view.search'; export const FindInFilesActionId = 'workbench.action.findInFiles'; export const FocusActiveEditorActionId = 'search.action.focusActiveEditor'; -export const FocusNextSearchResultActionId = 'search.action.focusNextSearchResult'; -export const FocusPreviousSearchResultActionId = 'search.action.focusPreviousSearchResult'; export const ToggleCaseSensitiveActionId = 'toggleSearchCaseSensitive'; export const ToggleWholeWordActionId = 'toggleSearchWholeWord'; From 13d401e458d8c72a19f3ebc01646f13eb720a41f Mon Sep 17 00:00:00 2001 From: roblou Date: Tue, 3 Jan 2017 20:09:19 -0800 Subject: [PATCH 059/131] Expose navigator methods so I can avoid calling selectNext multiple times per action- it has side effects --- src/vs/base/parts/tree/browser/tree.ts | 2 +- src/vs/base/parts/tree/browser/treeImpl.ts | 4 +- .../parts/search/browser/searchViewlet.ts | 45 ++++++++++--------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/vs/base/parts/tree/browser/tree.ts b/src/vs/base/parts/tree/browser/tree.ts index b59f4b267cc..10029064360 100644 --- a/src/vs/base/parts/tree/browser/tree.ts +++ b/src/vs/base/parts/tree/browser/tree.ts @@ -328,7 +328,7 @@ export interface ITree extends Events.IEventEmitter { * Returns a navigator which allows to discover the visible and * expanded elements in the tree. */ - getNavigator(): INavigator; + getNavigator(fromElement?: any, subTreeOnly?: boolean): INavigator; /** * Disposes the tree diff --git a/src/vs/base/parts/tree/browser/treeImpl.ts b/src/vs/base/parts/tree/browser/treeImpl.ts index 3e8879bdb90..d7453bc6b7b 100644 --- a/src/vs/base/parts/tree/browser/treeImpl.ts +++ b/src/vs/base/parts/tree/browser/treeImpl.ts @@ -318,8 +318,8 @@ export class Tree extends Events.EventEmitter implements _.ITree { return this.model.hasTrait(trait, element); } - getNavigator(): INavigator { - return new MappedNavigator(this.model.getNavigator(), i => i && i.getElement()); + getNavigator(fromElement?: any, subTreeOnly?: boolean): INavigator { + return new MappedNavigator(this.model.getNavigator(fromElement, subTreeOnly), i => i && i.getElement()); } public dispose(): void { diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts index b0580881909..748b1904ff2 100644 --- a/src/vs/workbench/parts/search/browser/searchViewlet.ts +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -451,52 +451,53 @@ export class SearchViewlet extends Viewlet { public selectNextResult(): void { const eventPayload = { focusEditor: true }; + const [selected]: FileMatchOrMatch[] = this.tree.getSelection(); + const navigator = this.tree.getNavigator(selected, /*subTreeOnly=*/false); + let next = navigator.next(); - this.tree.selectNext(undefined, undefined, eventPayload); - let [selected]: FileMatchOrMatch[] = this.tree.getSelection(); - if (!selected) { + if (!next) { return; } // Expand and go past FileMatch nodes - if (!(selected instanceof Match)) { - if (!this.tree.isExpanded(selected)) { - this.tree.expand(selected); + if (!(next instanceof Match)) { + if (!this.tree.isExpanded(next)) { + this.tree.expand(next); } // Select the FileMatch's first child - this.tree.selectNext(undefined, undefined, eventPayload); + next = navigator.next(); } // Reveal the newly selected element - [selected] = this.tree.getSelection(); - this.tree.reveal(selected); + this.tree.setSelection([next], eventPayload); + this.tree.reveal(next); } public selectPreviousResult(): void { const eventPayload = { focusEditor: true }; + const [selected]: FileMatchOrMatch[] = this.tree.getSelection(); + const navigator = this.tree.getNavigator(selected, /*subTreeOnly=*/false); - this.tree.selectPrevious(undefined, undefined, eventPayload); - let [selected]: FileMatchOrMatch[] = this.tree.getSelection(); - if (!selected) { + let prev = navigator.previous(); + if (!prev) { return; } // Expand and go past FileMatch nodes - if (!(selected instanceof Match)) { - this.tree.selectPrevious(undefined, undefined, eventPayload); - [selected] = this.tree.getSelection(); - - if (!(selected instanceof Match)) { - this.tree.selectNext(undefined, undefined, eventPayload); - this.tree.expand(selected); - this.tree.selectPrevious(undefined, undefined, eventPayload); + if (!(prev instanceof Match)) { + prev = navigator.previous(); + if (!(prev instanceof Match)) { + // There is a second non-Match result, which must be a collapsed FileMatch. + // Expand it then select its last child. + navigator.next(); + this.tree.expand(prev); + prev = navigator.previous(); } } // Reveal the newly selected element - [selected] = this.tree.getSelection(); - this.tree.reveal(selected); + this.tree.setSelection([prev], eventPayload); } public setVisible(visible: boolean): TPromise { From 23e2f1678f136d2de1333e02ca12ac0ed156f9e4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 4 Jan 2017 07:56:08 +0100 Subject: [PATCH 060/131] use latest version id when triggering next save --- .../textfile/common/textFileEditorModel.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index e8c3d489d3d..1353b624115 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -547,27 +547,19 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return TPromise.as(null); } - // Return if currently saving by scheduling another auto save if enabled or storing this version id as next save. + // Return if currently saving by storing this save request as the next save that should happen. // Never ever must 2 saves execute at the same time because this can lead to dirty writes and race conditions. // // Scenario A: auto save was triggered and is currently busy saving to disk. this takes long enough that another auto save - // kicks in. since we never want to trigger 2 saves at the same time, we push out this auto save for the - // configured auto save delay assuming that it can proceed next time it triggers. + // kicks in. // Scenario B: save is very slow (e.g. network share) and the user manages to change the buffer and trigger another save // while the first save has not returned yet. // if (this.saveSequentializer.hasPendingSave()) { diag(`doSave(${versionId}) - exit - because busy saving`, this.resource, new Date()); - // Trigger another auto save if enabled - if (this.autoSaveAfterMilliesEnabled) { - return this.doAutoSave(versionId); - } - - // Otherwise register this as the next upcoming save and return - else { - return this.saveSequentializer.setNext(() => this.doSave(versionId, reason, overwriteReadonly, overwriteEncoding)); - } + // Register this as the next upcoming save and return + return this.saveSequentializer.setNext(() => this.doSave(this.versionId /* make sure to use latest version id here */, reason, overwriteReadonly, overwriteEncoding)); } // Push all edit operations to the undo stack so that the user has a chance to From 1768e0f6a3d4e09abc089b5bb4372933844482a2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 4 Jan 2017 08:34:19 +0100 Subject: [PATCH 061/131] Unable to next/previous via keybindings when diff editor is not focused (fixes #14124) --- .../browser/parts/editor/editorCommands.ts | 4 ++-- .../browser/parts/editor/editorPart.ts | 18 +++++++++++++++++- .../browser/parts/editor/textDiffEditor.ts | 15 --------------- src/vs/workbench/common/editor.ts | 3 +++ 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 3ea08428627..fc5ba177a1a 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -8,11 +8,11 @@ import * as types from 'vs/base/common/types'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; -import { ActiveEditorMoveArguments, ActiveEditorMovePositioning, ActiveEditorMovePositioningBy, EditorCommands } from 'vs/workbench/common/editor'; +import { ActiveEditorMoveArguments, ActiveEditorMovePositioning, ActiveEditorMovePositioningBy, EditorCommands, TextCompareEditorVisible } from 'vs/workbench/common/editor'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditor, Position, POSITIONS } from 'vs/platform/editor/common/editor'; import { EditorContextKeys } from 'vs/editor/common/editorCommon'; -import { TextCompareEditorVisible, TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor'; +import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor'; import { EditorStacksModel } from 'vs/workbench/common/editor/editorStacksModel'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IMessageService, Severity, CloseAction } from 'vs/platform/message/common/message'; diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index a087d318f48..162ea2b23e7 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -21,7 +21,7 @@ import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Scope as MementoScope } from 'vs/workbench/common/memento'; import { Part } from 'vs/workbench/browser/part'; import { BaseEditor, EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { IEditorRegistry, Extensions as EditorExtensions, EditorInput, EditorOptions, ConfirmResult, IWorkbenchEditorConfiguration, IEditorDescriptor, TextEditorOptions, SideBySideEditorInput } from 'vs/workbench/common/editor'; +import { IEditorRegistry, Extensions as EditorExtensions, EditorInput, EditorOptions, ConfirmResult, IWorkbenchEditorConfiguration, IEditorDescriptor, TextEditorOptions, SideBySideEditorInput, TextCompareEditorVisible, TEXT_DIFF_EDITOR_ID } from 'vs/workbench/common/editor'; import { EditorGroupsControl, Rochade, IEditorGroupsControl, ProgressState } from 'vs/workbench/browser/parts/editor/editorGroupsControl'; import { WorkbenchProgressService } from 'vs/workbench/services/progress/browser/progressService'; import { IEditorGroupService, GroupOrientation, GroupArrangement, ITabOptions } from 'vs/workbench/services/group/common/groupService'; @@ -37,6 +37,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { EditorStacksModel, EditorGroup, EditorIdentifier, GroupEvent } from 'vs/workbench/common/editor/editorStacksModel'; import Event, { Emitter } from 'vs/base/common/event'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; class ProgressMonitor { @@ -93,6 +94,8 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService private _onGroupOrientationChanged: Emitter; private _onTabOptionsChanged: Emitter; + private textCompareEditorVisible: IContextKey; + // The following data structures are partitioned into array of Position as provided by Services.POSITION array private visibleEditors: BaseEditor[]; private instantiatedEditors: BaseEditor[][]; @@ -109,6 +112,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService @IStorageService private storageService: IStorageService, @IPartService private partService: IPartService, @IConfigurationService private configurationService: IConfigurationService, + @IContextKeyService contextKeyService: IContextKeyService, @IInstantiationService private instantiationService: IInstantiationService ) { super(id); @@ -132,6 +136,8 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService this.stacks = this.instantiationService.createInstance(EditorStacksModel, restoreFromStorage); + this.textCompareEditorVisible = TextCompareEditorVisible.bindTo(contextKeyService); + const config = configurationService.getConfiguration(); if (config && config.workbench && config.workbench.editor) { const editorConfig = config.workbench.editor; @@ -350,6 +356,9 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService // Indicate to editor that it is now visible editor.setVisible(true, position); + // Update text compare editor visible context + this.updateTextCompareEditorVisible(); + // Make sure the editor is layed out this.editorGroupsControl.layout(position); @@ -595,6 +604,9 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService editor.clearInput(); editor.setVisible(false); + // Update text compare editor visible context + this.updateTextCompareEditorVisible(); + // Clear active editor this.visibleEditors[position] = null; @@ -607,6 +619,10 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService } } + private updateTextCompareEditorVisible(): void { + this.textCompareEditorVisible.set(this.visibleEditors.some(e => e && e.isVisible() && e.getId() === TEXT_DIFF_EDITOR_ID)); + } + public closeAllEditors(except?: Position): TPromise { let groups = this.stacks.groups.reverse(); // start from the end to prevent layout to happen through rochade diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 246a4d3948d..621985ff420 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -12,7 +12,6 @@ import { Builder } from 'vs/base/browser/builder'; import { Action, IAction } from 'vs/base/common/actions'; import { onUnexpectedError } from 'vs/base/common/errors'; import types = require('vs/base/common/types'); -import { Position } from 'vs/platform/editor/common/editor'; import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/editorCommon'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; @@ -31,13 +30,10 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IThemeService } from 'vs/workbench/services/themes/common/themeService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; -export const TextCompareEditorVisible = new RawContextKey('textCompareEditorVisible', false); - /** * The text editor that leverages the diff text editor for the editing experience. */ @@ -49,22 +45,17 @@ export class TextDiffEditor extends BaseTextEditor { private nextDiffAction: NavigateAction; private previousDiffAction: NavigateAction; - private textDiffEditorVisible: IContextKey; - constructor( @ITelemetryService telemetryService: ITelemetryService, @IInstantiationService instantiationService: IInstantiationService, @IStorageService storageService: IStorageService, @IConfigurationService configurationService: IConfigurationService, @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @IContextKeyService contextKeyService: IContextKeyService, @IThemeService themeService: IThemeService, @IEditorGroupService private editorGroupService: IEditorGroupService, @ITextFileService textFileService: ITextFileService ) { super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService); - - this.textDiffEditorVisible = TextCompareEditorVisible.bindTo(contextKeyService); } public getTitle(): string { @@ -263,12 +254,6 @@ export class TextDiffEditor extends BaseTextEditor { super.clearInput(); } - public setEditorVisible(visible: boolean, position: Position): void { - this.textDiffEditorVisible.set(visible); - - super.setEditorVisible(visible, position); - } - public getDiffNavigator(): DiffNavigator { return this.diffNavigator; } diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 927e46b9ef1..4717f2ec35c 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -16,6 +16,9 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { SyncDescriptor, AsyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export const TextCompareEditorVisible = new RawContextKey('textCompareEditorVisible', false); export enum ConfirmResult { SAVE, From c6a320c4f9c53b809a9615324a5b563eeb217984 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 4 Jan 2017 10:03:23 +0100 Subject: [PATCH 062/131] fix #18055 --- src/vs/base/common/lifecycle.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index 4aeaf4a743b..415b949b75d 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -20,9 +20,11 @@ export function dispose(first: T | T[], ...rest: T[]): T if (Array.isArray(first)) { first.forEach(d => d && d.dispose()); return []; - } else if (rest.length === 0 && first) { - first.dispose(); - return first; + } else if (rest.length === 0) { + if (first) { + first.dispose(); + return first; + } } else { dispose(first); dispose(rest); From ada971a483b2b4ec662848134498b3c4de560b35 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 4 Jan 2017 10:33:48 +0100 Subject: [PATCH 063/131] Crash in extension reading configuration values on startup (fixes #17807) --- src/vs/platform/configuration/common/configuration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index 009e6a78feb..fe9e02d2647 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -82,7 +82,7 @@ export function getConfigurationValue(config: any, settingPath: string, defau let current = config; for (let i = 0; i < path.length; i++) { current = current[path[i]]; - if (typeof current === 'undefined') { + if (typeof current === 'undefined' || current === null) { return undefined; } } From 8b834d0f890f62ce25684f6b371559b4f4f6a223 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 4 Jan 2017 11:01:22 +0100 Subject: [PATCH 064/131] Search relevance in Quick Pick control (fixes #18003) --- .../parts/quickopen/browser/quickOpenModel.ts | 10 +++++----- .../parts/quickopen/quickOpenController.ts | 18 ++++++++++++++++-- .../quickopen/browser/gotoSymbolHandler.ts | 8 ++++---- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 18e6deabcbe..4c36e669bd3 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -689,15 +689,15 @@ export class QuickOpenModel implements return this._entries; } - getId(entry: QuickOpenEntry): string { + public getId(entry: QuickOpenEntry): string { return entry.getId(); } - getLabel(entry: QuickOpenEntry): string { + public getLabel(entry: QuickOpenEntry): string { return entry.getLabel(); } - getAriaLabel(entry: QuickOpenEntry): string { + public getAriaLabel(entry: QuickOpenEntry): string { const ariaLabel = entry.getAriaLabel(); if (ariaLabel) { return nls.localize('quickOpenAriaLabelEntry', "{0}, picker", entry.getAriaLabel()); @@ -706,11 +706,11 @@ export class QuickOpenModel implements return nls.localize('quickOpenAriaLabel', "picker"); } - isVisible(entry: QuickOpenEntry): boolean { + public isVisible(entry: QuickOpenEntry): boolean { return !entry.isHidden(); } - run(entry: QuickOpenEntry, mode: Mode, context: IContext): boolean { + public run(entry: QuickOpenEntry, mode: Mode, context: IContext): boolean { return entry.run(mode, context); } } diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index c0ff71dd522..c99ab5c20ac 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -342,9 +342,9 @@ export class QuickOpenController extends WorkbenchComponent implements IQuickOpe // Model const model = new QuickOpenModel(); - const entries = picks.map(e => this.instantiationService.createInstance(PickOpenEntry, e, () => progress(e))); + const entries = picks.map((e, index) => this.instantiationService.createInstance(PickOpenEntry, e, index, () => progress(e))); if (picks.length === 0) { - entries.push(this.instantiationService.createInstance(PickOpenEntry, { label: nls.localize('emptyPicks', "There are no entries to pick from") }, null)); + entries.push(this.instantiationService.createInstance(PickOpenEntry, { label: nls.localize('emptyPicks', "There are no entries to pick from") }, 0, null)); } model.setEntries(entries); @@ -414,6 +414,15 @@ export class QuickOpenController extends WorkbenchComponent implements IQuickOpe }); } + // Sort by value + model.entries.sort((pickA: PickOpenEntry, pickB: PickOpenEntry) => { + if (!value) { + return pickA.index - pickB.index; // restore natural order + } + + return QuickOpenEntry.compare(pickA, pickB, value); + }); + this.pickOpenWidget.refresh(model, value ? { autoFocusFirstEntry: true } : autoFocus); }, onShow: () => this.handleOnShow(true), @@ -1001,6 +1010,7 @@ class PickOpenEntry extends PlaceholderQuickOpenEntry { constructor( item: IPickOpenEntry, + private _index: number, private onPreview: () => void, @IModeService private modeService: IModeService, @IModelService private modelService: IModelService @@ -1018,6 +1028,10 @@ class PickOpenEntry extends PlaceholderQuickOpenEntry { this.isFolder = fileItem.isFolder; } + public get index(): number { + return this._index; + } + public getLabelOptions(): IIconLabelOptions { return { extraClasses: this.resource ? getIconClasses(this.modelService, this.modeService, this.resource, this.isFolder) : [] diff --git a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts index 501d38dedd4..18eabe4b04d 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts @@ -46,7 +46,7 @@ class OutlineModel extends QuickOpenModel { this.outline = outline; } - public dofilter(searchValue: string): void { + public applyFilter(searchValue: string): void { // Normalize search let normalizedSearchValue = searchValue; @@ -389,10 +389,10 @@ export class GotoSymbolHandler extends QuickOpenHandler { } // Resolve Outline Model - return this.getActiveOutline().then((outline) => { + return this.getActiveOutline().then(outline => { // Filter by search - outline.dofilter(searchValue); + outline.applyFilter(searchValue); return outline; }); @@ -548,7 +548,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { public clearDecorations(): void { if (this.rangeHighlightDecorationId) { - this.editorService.getVisibleEditors().forEach((editor) => { + this.editorService.getVisibleEditors().forEach(editor => { if (editor.position === this.rangeHighlightDecorationId.position) { const editorControl = editor.getControl(); editorControl.changeDecorations((changeAccessor: IModelDecorationsChangeAccessor) => { From 98374166016bdfd3793ab5127ff4017e374a9b5c Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Wed, 4 Jan 2017 12:08:28 +0100 Subject: [PATCH 065/131] Update copyright year to 2017 --- build/gulpfile.vscode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index e0359c5d0eb..846b10ecdce 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -110,7 +110,7 @@ const config = { version: packageJson.electronVersion, productAppName: product.nameLong, companyName: 'Microsoft Corporation', - copyright: 'Copyright (C) 2016 Microsoft. All rights reserved', + copyright: 'Copyright (C) 2017 Microsoft. All rights reserved', darwinIcon: 'resources/darwin/code.icns', darwinBundleIdentifier: product.darwinBundleIdentifier, darwinApplicationCategoryType: 'public.app-category.developer-tools', From cafc25cc99856e6fdf15ecfba486fbc51e0bd63d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 4 Jan 2017 12:57:11 +0100 Subject: [PATCH 066/131] Tabs: changing close button enablement no longer updates live (fixes #18101) --- .../browser/parts/editor/tabsTitleControl.ts | 15 ++++++--------- .../browser/parts/editor/titleControl.ts | 2 -- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 7ede1f4eb2b..ac4094b5fe1 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -19,7 +19,6 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { EditorLabel } from 'vs/workbench/browser/labels'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; @@ -59,7 +58,6 @@ export class TabsTitleControl extends TitleControl { constructor( @IContextMenuService contextMenuService: IContextMenuService, @IInstantiationService instantiationService: IInstantiationService, - @IConfigurationService configurationService: IConfigurationService, @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IEditorGroupService editorGroupService: IEditorGroupService, @IUntitledEditorService private untitledEditorService: IUntitledEditorService, @@ -71,7 +69,7 @@ export class TabsTitleControl extends TitleControl { @IQuickOpenService quickOpenService: IQuickOpenService, @IWindowService private windowService: IWindowService ) { - super(contextMenuService, instantiationService, configurationService, editorService, editorGroupService, contextKeyService, keybindingService, telemetryService, messageService, menuService, quickOpenService); + super(contextMenuService, instantiationService, editorService, editorGroupService, contextKeyService, keybindingService, telemetryService, messageService, menuService, quickOpenService); this.tabDisposeables = []; this.editorLabels = []; @@ -218,6 +216,11 @@ export class TabsTitleControl extends TitleControl { // Container tabContainer.setAttribute('aria-label', `${name}, tab`); tabContainer.title = verboseDescription; + if (this.tabOptions.showTabCloseButton) { + DOM.removeClass(tabContainer, 'no-close-button'); + } else { + DOM.addClass(tabContainer, 'no-close-button'); + } // Label const tabLabel = this.editorLabels[index]; @@ -362,12 +365,6 @@ export class TabsTitleControl extends TitleControl { tabContainer.setAttribute('role', 'presentation'); // cannot use role "tab" here due to https://github.com/Microsoft/vscode/issues/8659 DOM.addClass(tabContainer, 'tab monaco-editor-background'); - if (!this.tabOptions.showTabCloseButton) { - DOM.addClass(tabContainer, 'no-close-button'); - } else { - DOM.removeClass(tabContainer, 'no-close-button'); - } - // Tab Editor Label const editorLabel = this.instantiationService.createInstance(EditorLabel, tabContainer, void 0); this.editorLabels.push(editorLabel); diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index bc9e500d594..bd997951c32 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -23,7 +23,6 @@ import { IActionItem, ActionsOrientation, Separator } from 'vs/base/browser/ui/a import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorGroupService, ITabOptions } from 'vs/workbench/services/group/common/groupService'; import { IMessageService, Severity } from 'vs/platform/message/common/message'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; @@ -94,7 +93,6 @@ export abstract class TitleControl implements ITitleAreaControl { constructor( @IContextMenuService protected contextMenuService: IContextMenuService, @IInstantiationService protected instantiationService: IInstantiationService, - @IConfigurationService protected configurationService: IConfigurationService, @IWorkbenchEditorService protected editorService: IWorkbenchEditorService, @IEditorGroupService protected editorGroupService: IEditorGroupService, @IContextKeyService protected contextKeyService: IContextKeyService, From c0cfb1a665482258ace6d625e30e05ac76b88c23 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 4 Jan 2017 15:58:39 +0100 Subject: [PATCH 067/131] :lipstick: --- src/vs/base/common/arrays.ts | 4 ++-- src/vs/base/common/async.ts | 8 +++---- src/vs/base/common/dates.ts | 38 --------------------------------- src/vs/base/common/map.ts | 6 +++--- src/vs/base/common/mime.ts | 4 ++-- src/vs/base/common/network.ts | 12 +++++------ src/vs/base/common/paths.ts | 10 ++++----- src/vs/base/common/stopwatch.ts | 2 +- src/vs/base/common/strings.ts | 4 ++-- 9 files changed, 25 insertions(+), 63 deletions(-) delete mode 100644 src/vs/base/common/dates.ts diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 2eb260b91bb..176cc70a501 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -18,7 +18,7 @@ export function equals(one: T[], other: T[], itemEquals: (a: T, b: T) => bool return false; } - for (var i = 0, len = one.length; i < len; i++) { + for (let i = 0, len = one.length; i < len; i++) { if (!itemEquals(one[i], other[i])) { return false; } @@ -177,7 +177,7 @@ export function first(array: T[], fn: (item: T) => boolean, notFoundValue: T export function commonPrefixLength(one: T[], other: T[], equals: (a: T, b: T) => boolean = (a, b) => a === b): number { let result = 0; - for (var i = 0, len = Math.min(one.length, other.length); i < len && equals(one[i], other[i]); i++) { + for (let i = 0, len = Math.min(one.length, other.length); i < len && equals(one[i], other[i]); i++) { result++; } diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index b899d0f7350..fadef1d89ea 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -71,8 +71,8 @@ export interface ITask { * The throttler implements this via the queue() method, by providing it a task * factory. Following the example: * - * var throttler = new Throttler(); - * var letters = []; + * const throttler = new Throttler(); + * const letters = []; * * function deliver() { * const lettersToDeliver = letters; @@ -166,8 +166,8 @@ export class SimpleThrottler { * to be executed and the waiting period (delay) must be passed in as arguments. Following * the example: * - * var delayer = new Delayer(WAITING_PERIOD); - * var letters = []; + * const delayer = new Delayer(WAITING_PERIOD); + * const letters = []; * * function letterReceived(l) { * letters.push(l); diff --git a/src/vs/base/common/dates.ts b/src/vs/base/common/dates.ts deleted file mode 100644 index 8a442c17f1a..00000000000 --- a/src/vs/base/common/dates.ts +++ /dev/null @@ -1,38 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import nls = require('vs/nls'); - -export function since(date: Date): string { - var seconds = (new Date().getTime() - date.getTime()) / 1000; - if (seconds < 60) { - return nls.localize('diff.seconds.verbose', "just now"); - } - - var minutes = seconds / 60; - if (minutes < 60) { - return Math.floor(minutes) === 1 ? nls.localize('diff.minute.verbose', "1 minute ago") : nls.localize('diff.minutes.verbose', "{0} minutes ago", Math.floor(minutes)); - } - - var hours = minutes / 60; - if (hours < 24) { - return Math.floor(hours) === 1 ? nls.localize('diff.hour.verbose', "1 hour ago") : nls.localize('diff.hours.verbose', "{0} hours ago", Math.floor(hours)); - } - - var days = hours / 24; - if (Math.floor(days) === 1) { - return nls.localize('diff.days.yesterday', "yesterday"); - } - - if (days > 6 && days < 8) { - return nls.localize('diff.days.week', "a week ago"); - } - - if (days > 30 && days < 40) { - return nls.localize('diff.days.month', "a month ago"); - } - - return nls.localize('diff.days.verbose', "{0} days ago", Math.floor(days)); -} \ No newline at end of file diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 3906356d003..0eb5f89db37 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -52,7 +52,7 @@ export class LinkedMap { } public keys(): K[] { - var keys: K[] = []; + const keys: K[] = []; for (let key in this.map) { keys.push(this.map[key].key); } @@ -60,7 +60,7 @@ export class LinkedMap { } public values(): T[] { - var values: T[] = []; + const values: T[] = []; for (let key in this.map) { values.push(this.map[key].value); } @@ -68,7 +68,7 @@ export class LinkedMap { } public entries(): Entry[] { - var entries: Entry[] = []; + const entries: Entry[] = []; for (let key in this.map) { entries.push(this.map[key]); } diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index d47010f4e0b..f05d9576d9b 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -143,7 +143,7 @@ function guessMimeTypeByPath(path: string, filename: string, associations: IText let patternMatch: ITextMimeAssociationItem; let extensionMatch: ITextMimeAssociationItem; - for (var i = 0; i < associations.length; i++) { + for (let i = 0; i < associations.length; i++) { let association = associations[i]; // First exact name match @@ -243,7 +243,7 @@ export function isUnspecific(mime: string[] | string): boolean { } export function suggestFilename(langId: string, prefix: string): string { - for (var i = 0; i < registeredAssociations.length; i++) { + for (let i = 0; i < registeredAssociations.length; i++) { let association = registeredAssociations[i]; if (association.userConfigured) { continue; // only support registered ones diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index ee18a5ec9d2..2bd0f2a3d45 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -12,23 +12,23 @@ export namespace Schemas { * A schema that is used for models that exist in memory * only and that have no correspondence on a server or such. */ - export var inMemory: string = 'inmemory'; + export const inMemory: string = 'inmemory'; /** * A schema that is used for setting files */ - export var vscode: string = 'vscode'; + export const vscode: string = 'vscode'; /** * A schema that is used for internal private files */ - export var internal: string = 'private'; + export const internal: string = 'private'; - export var http: string = 'http'; + export const http: string = 'http'; - export var https: string = 'https'; + export const https: string = 'https'; - export var file: string = 'file'; + export const file: string = 'file'; } export interface IXHROptions { diff --git a/src/vs/base/common/paths.ts b/src/vs/base/common/paths.ts index 084eacd9782..92de20b38ef 100644 --- a/src/vs/base/common/paths.ts +++ b/src/vs/base/common/paths.ts @@ -11,12 +11,12 @@ import { CharCode } from 'vs/base/common/charCode'; /** * The forward slash path separator. */ -export var sep = '/'; +export const sep = '/'; /** * The native path separator depending on the OS. */ -export var nativeSep = isWindows ? '\\' : '/'; +export const nativeSep = isWindows ? '\\' : '/'; export function relative(from: string, to: string): string { const originalNormalizedFrom = normalize(from); @@ -50,7 +50,7 @@ export function relative(from: string, to: string): string { * @returns the directory name of a path. */ export function dirname(path: string): string { - var idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\'); + const idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\'); if (idx === 0) { return '.'; } else if (~idx === 0) { @@ -64,7 +64,7 @@ export function dirname(path: string): string { * @returns the base name of a path. */ export function basename(path: string): string { - var idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\'); + const idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\'); if (idx === 0) { return path; } else if (~idx === path.length - 1) { @@ -79,7 +79,7 @@ export function basename(path: string): string { */ export function extname(path: string): string { path = basename(path); - var idx = ~path.lastIndexOf('.'); + const idx = ~path.lastIndexOf('.'); return idx ? path.substring(~idx) : ''; } diff --git a/src/vs/base/common/stopwatch.ts b/src/vs/base/common/stopwatch.ts index 4f0fc157da3..fe3889b2918 100644 --- a/src/vs/base/common/stopwatch.ts +++ b/src/vs/base/common/stopwatch.ts @@ -6,7 +6,7 @@ import { globals } from 'vs/base/common/platform'; -var hasPerformanceNow = (globals.performance && typeof globals.performance.now === 'function'); +const hasPerformanceNow = (globals.performance && typeof globals.performance.now === 'function'); export class StopWatch { diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index f8b5625a737..fb4c42cd452 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -607,8 +607,8 @@ export function safeBtoa(str: string): string { } export function repeat(s: string, count: number): string { - var result = ''; - for (var i = 0; i < count; i++) { + let result = ''; + for (let i = 0; i < count; i++) { result += s; } return result; From b46de2cd570d1e4bfa9042fcfcdccca69635bda2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 4 Jan 2017 13:03:25 +0100 Subject: [PATCH 068/131] Do not clear ranges if editor model is not tehre --- src/vs/workbench/common/editor/rangeDecorations.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/common/editor/rangeDecorations.ts b/src/vs/workbench/common/editor/rangeDecorations.ts index cecec707c43..62c1ccd9ec1 100644 --- a/src/vs/workbench/common/editor/rangeDecorations.ts +++ b/src/vs/workbench/common/editor/rangeDecorations.ts @@ -96,8 +96,10 @@ export class RangeHighlightDecorations implements IDisposable { } public dispose() { - this.removeHighlightRange(); - this.disposeEditorListeners(); - this.editor = null; + if (this.editor && this.editor.getModel()) { + this.removeHighlightRange(); + this.disposeEditorListeners(); + this.editor = null; + } } } \ No newline at end of file From e85c2ccce0b352ef16ba85781bca47af1355e898 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 4 Jan 2017 13:03:43 +0100 Subject: [PATCH 069/131] Tests for filters --- src/vs/base/test/common/filters.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/base/test/common/filters.test.ts b/src/vs/base/test/common/filters.test.ts index 21eae022f5b..4da6a3a3b36 100644 --- a/src/vs/base/test/common/filters.test.ts +++ b/src/vs/base/test/common/filters.test.ts @@ -186,5 +186,8 @@ suite('Filters', () => { filterOk(matchesWords, 'git プル', 'git: プル', [{ start: 0, end: 3 }, { start: 4, end: 7 }]); filterOk(matchesWords, 'öäk', 'Öhm: Älles Klar', [{ start: 0, end: 1 }, { start: 5, end: 6 }, { start: 11, end: 12 }]); + + assert.ok(matchesWords('gipu', 'Category: Git: Pull', true) === null); + assert.deepEqual(matchesWords('pu', 'Category: Git: Pull', true), [{ start: 15, end: 17 }]); }); }); From 618dbf478546497fd04a20ef8a3c57d534276a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 4 Jan 2017 16:12:52 +0100 Subject: [PATCH 070/131] improve list accessibility fixes #17113 --- src/vs/base/browser/ui/list/listPaging.ts | 7 ++++--- src/vs/base/browser/ui/list/listWidget.ts | 20 ++++++++++++++++--- .../extensions/browser/extensionsList.ts | 5 ++++- .../electron-browser/extensionsViewlet.ts | 6 ++++-- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index c5ceceb4cf4..2c36a2f8012 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -7,7 +7,7 @@ import 'vs/css!./list'; import { IDisposable } from 'vs/base/common/lifecycle'; import { range } from 'vs/base/common/arrays'; import { IDelegate, IRenderer, IFocusChangeEvent, ISelectionChangeEvent } from './list'; -import { List } from './listWidget'; +import { List, IListOptions } from './listWidget'; import { IPagedModel } from 'vs/base/common/paging'; import Event, { mapEvent } from 'vs/base/common/event'; @@ -67,10 +67,11 @@ export class PagedList { constructor( container: HTMLElement, delegate: IDelegate, - renderers: IPagedRenderer[] + renderers: IPagedRenderer[], + options: IListOptions = {} ) { const pagedRenderers = renderers.map(r => new PagedRenderer>(r, () => this.model)); - this.list = new List(container, delegate, pagedRenderers); + this.list = new List(container, delegate, pagedRenderers, options); } get onFocusChange(): Event> { diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index f05b962354a..64ab0b7ed48 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -116,7 +116,7 @@ class FocusTrait extends Trait { renderElement(element: T, index: number, container: HTMLElement): void { super.renderElement(element, index, container); - container.setAttribute('role', 'option'); + container.setAttribute('role', 'treeitem'); container.setAttribute('id', this.getElementId(index)); } } @@ -201,6 +201,7 @@ class Controller implements IDisposable { } export interface IListOptions extends IListViewOptions { + ariaLabel?: string; } const DefaultOptions: IListOptions = {}; @@ -245,13 +246,17 @@ export class List implements IDisposable { }); this.view = new ListView(container, delegate, renderers, options); - this.view.domNode.setAttribute('role', 'listbox'); + this.view.domNode.setAttribute('role', 'tree'); this.view.domNode.tabIndex = 0; this.controller = new Controller(this, this.view); this.disposables = [this.focus, this.selection, this.view, this.controller]; this._onDOMFocus = domEvent(this.view.domNode, 'focus'); this.onFocusChange(this._onFocusChange, this, this.disposables); + + if (options.ariaLabel) { + this.view.domNode.setAttribute('aria-label', options.ariaLabel); + } } splice(start: number, deleteCount: number, ...elements: T[]): void { @@ -418,7 +423,16 @@ export class List implements IDisposable { } private _onFocusChange(): void { - DOM.toggleClass(this.view.domNode, 'element-focused', this.focus.get().length > 0); + const focus = this.focus.get(); + + if (focus.length > 0) { + this.view.domNode.setAttribute('aria-activedescendant', this.getElementId(focus[0])); + } else { + this.view.domNode.removeAttribute('aria-activedescendant'); + } + + this.view.domNode.setAttribute('role', 'tree'); + DOM.toggleClass(this.view.domNode, 'element-focused', focus.length > 0); } dispose(): void { diff --git a/src/vs/workbench/parts/extensions/browser/extensionsList.ts b/src/vs/workbench/parts/extensions/browser/extensionsList.ts index c5dd01e143a..88bb1de5ce3 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsList.ts @@ -23,6 +23,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IExtensionService } from 'vs/platform/extensions/common/extensions'; export interface ITemplateData { + root: HTMLElement; element: HTMLElement; icon: HTMLImageElement; name: HTMLElement; @@ -92,7 +93,7 @@ export class Renderer implements IPagedRenderer { const disposables = [versionWidget, installCountWidget, ratingsWidget, builtinStatusAction, updateAction, reloadAction, manageAction, actionbar]; return { - element, icon, name, installCount, ratings, author, description, disposables, + root, element, icon, name, installCount, ratings, author, description, disposables, extensionDisposables: [], set extension(extension: IExtension) { versionWidget.extension = extension; @@ -110,6 +111,7 @@ export class Renderer implements IPagedRenderer { renderPlaceholder(index: number, data: ITemplateData): void { addClass(data.element, 'loading'); + data.root.removeAttribute('aria-label'); data.extensionDisposables = dispose(data.extensionDisposables); data.icon.src = ''; data.name.textContent = ''; @@ -142,6 +144,7 @@ export class Renderer implements IPagedRenderer { data.icon.style.visibility = 'inherit'; } + data.root.setAttribute('aria-label', extension.displayName); data.name.textContent = extension.displayName; data.author.textContent = extension.publisherDisplayName; data.description.textContent = extension.description; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index 08263b5fe23..6ee74bb6e7a 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -102,7 +102,9 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet { const delegate = new Delegate(); const renderer = this.instantiationService.createInstance(Renderer); - this.list = new PagedList(this.extensionsBox, delegate, [renderer]); + this.list = new PagedList(this.extensionsBox, delegate, [renderer], { + ariaLabel: localize('extensions', "Extensions") + }); const onKeyDown = chain(domEvent(this.searchBox, 'keydown')) .map(e => new StandardKeyboardEvent(e)); @@ -434,7 +436,7 @@ export class StatusUpdater implements IWorkbenchContribution { private onServiceChange(): void { if (this.extensionsWorkbenchService.local.some(e => e.state === ExtensionState.Installing)) { - this.activityBarService.showActivity(VIEWLET_ID, new ProgressBadge(() => localize('extensions', 'Extensions')), 'extensions-badge progress-badge'); + this.activityBarService.showActivity(VIEWLET_ID, new ProgressBadge(() => localize('extensions', "Extensions")), 'extensions-badge progress-badge'); return; } From 0ebe04eb16d5636dfa74e2dda022ded0304f16b2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 4 Jan 2017 13:13:39 +0100 Subject: [PATCH 071/131] Show sub context menu if a line has more than one setting --- src/vs/workbench/parts/preferences/browser/preferencesEditor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index b2c3eac01c2..a1a79db6826 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -884,7 +884,7 @@ class EditSettingRenderer extends Disposable { private onEditSettingClicked(editPreferenceWidget: EditPreferenceWidget): void { const elementPosition = DOM.getDomNodePagePosition(editPreferenceWidget.getDomNode()); const anchor = { x: elementPosition.left + elementPosition.width, y: elementPosition.top + elementPosition.height + 10 }; - const actions = editPreferenceWidget.preferences.length === 1 ? this.getActions(editPreferenceWidget.preferences[0], this.getConfigurationsMap()[editPreferenceWidget.preferences[0].key]) + const actions = this.getSettingsAtLineNumber(editPreferenceWidget.getLine()).length === 1 ? this.getActions(editPreferenceWidget.preferences[0], this.getConfigurationsMap()[editPreferenceWidget.preferences[0].key]) : editPreferenceWidget.preferences.map(setting => new ContextSubMenu(setting.key, this.getActions(setting, this.getConfigurationsMap()[setting.key]))); this.contextMenuService.showContextMenu({ getAnchor: () => anchor, From 7a6dfcf081863cdd6c6451bb67e8900c4d57c5dc Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 4 Jan 2017 15:13:46 +0100 Subject: [PATCH 072/131] Highlight whole line always --- .../parts/preferences/browser/preferencesEditor.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index a1a79db6826..bc7a744c14f 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -367,7 +367,7 @@ export class SettingsRenderer extends Disposable implements IPreferencesRenderer setting = this.settingsEditorModel.getSetting(setting.key); // TODO:@sandy Selection range should be template range this.editor.setSelection(setting.valueRange); - this.settingHighlighter.highlight(this.settingsEditorModel.getSetting(setting.key), false, true); + this.settingHighlighter.highlight(this.settingsEditorModel.getSetting(setting.key), true); } } @@ -736,7 +736,7 @@ class FilteredSettingsNavigationRenderer extends Disposable { public next(): ISetting { let setting = this.iterator.next() || this.iterator.first(); if (setting) { - this.settingHighlighter.highlight(setting, true, true); + this.settingHighlighter.highlight(setting, true); return setting; } return null; @@ -952,15 +952,14 @@ class SettingHighlighter extends Disposable { this.volatileHighlighter = this._register(instantiationService.createInstance(RangeHighlightDecorations)); } - highlight(setting: ISetting, isWholeLine: boolean = true, fix: boolean = false) { + highlight(setting: ISetting, fix: boolean = false) { this.volatileHighlighter.removeHighlightRange(); this.fixedHighlighter.removeHighlightRange(); const highlighter = fix ? this.fixedHighlighter : this.volatileHighlighter; highlighter.highlightRange({ - range: isWholeLine ? setting.valueRange : setting.range, - resource: this.editor.getModel().uri, - isWholeLine + range: setting.valueRange, + resource: this.editor.getModel().uri }, this.editor); this.editor.revealLinesInCenterIfOutsideViewport(setting.valueRange.startLineNumber, setting.valueRange.endLineNumber - 1); From 411d03dd354e45241081827436b49287aad282c8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 4 Jan 2017 17:06:36 +0100 Subject: [PATCH 073/131] :lipstick: better differentiation between triggering and refineing completions --- .../contrib/suggest/common/suggestModel.ts | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/src/vs/editor/contrib/suggest/common/suggestModel.ts b/src/vs/editor/contrib/suggest/common/suggestModel.ts index fc522397f66..a78e1d41fb6 100644 --- a/src/vs/editor/contrib/suggest/common/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/common/suggestModel.ts @@ -274,12 +274,11 @@ export class SuggestModel implements IDisposable { } private onCursorChange(e: ICursorSelectionChangedEvent): void { - if (!e.selection.isEmpty()) { - this.cancel(); - return; - } - if (e.source !== 'keyboard' || e.reason !== CursorChangeReason.NotSet) { + if (!e.selection.isEmpty() + || e.source !== 'keyboard' + || e.reason !== CursorChangeReason.NotSet) { + this.cancel(); return; } @@ -288,32 +287,29 @@ export class SuggestModel implements IDisposable { return; } - const isInactive = this.state === State.Idle; - - if (isInactive && !this.editor.getConfiguration().contribInfo.quickSuggestions) { - return; - } - const model = this.editor.getModel(); - if (!model) { return; } const ctx = new Context(model, this.editor.getPosition(), false); - if (isInactive) { - // trigger was not called or it was canceled - this.cancel(); + if (this.state === State.Idle) { - if (ctx.shouldAutoTrigger()) { - this.triggerAutoSuggestPromise = TPromise.timeout(this.quickSuggestDelay); - this.triggerAutoSuggestPromise.then(() => { - this.triggerAutoSuggestPromise = null; - this.trigger(true); - }); + if (this.editor.getConfiguration().contribInfo.quickSuggestions) { + // trigger suggest from idle when configured to do so + this.cancel(); + if (ctx.shouldAutoTrigger()) { + this.triggerAutoSuggestPromise = TPromise.timeout(this.quickSuggestDelay); + this.triggerAutoSuggestPromise.then(() => { + this.triggerAutoSuggestPromise = null; + this.trigger(true); + }); + } } + } else { + // refine active suggestion this.onNewContext(ctx); } } From 8f88ee2a9e3f4587deda1de90e8a0345c708c753 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 4 Jan 2017 17:35:51 +0100 Subject: [PATCH 074/131] fixes #17951 --- src/vs/workbench/parts/debug/common/debugModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index 23a4ebba24f..684445fd374 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -71,7 +71,7 @@ export class OutputNameValueElement extends AbstractOutputElement implements deb } public get hasChildren(): boolean { - return isObject(this.valueObj) && Object.getOwnPropertyNames(this.valueObj).length > 0; + return (Array.isArray(this.valueObj) && this.valueObj.length > 0) || (isObject(this.valueObj) && Object.getOwnPropertyNames(this.valueObj).length > 0); } public getChildren(): TPromise { From 549845ed3a4d3bc557d2217f39f08d74979f7db0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 4 Jan 2017 17:15:09 +0100 Subject: [PATCH 075/131] suggest - move shouldAutoTrigger into static and reduce Context properties --- .../contrib/suggest/common/suggestModel.ts | 48 ++++++++----------- .../suggest/test/common/suggestModel.test.ts | 8 ++-- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/vs/editor/contrib/suggest/common/suggestModel.ts b/src/vs/editor/contrib/suggest/common/suggestModel.ts index a78e1d41fb6..08bbaeb674b 100644 --- a/src/vs/editor/contrib/suggest/common/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/common/suggestModel.ts @@ -32,14 +32,31 @@ export interface ISuggestEvent { export class Context { + static shouldAutoTrigger(editor: ICommonCodeEditor): boolean { + const model = editor.getModel(); + if (!model) { + return false; + } + const pos = editor.getPosition(); + const word = model.getWordAtPosition(pos); + if (!word) { + return false; + } + if (word.endColumn !== pos.column) { + return false; + } + if (!isNaN(Number(word.word))) { + return false; + } + return true; + } + readonly lineNumber: number; readonly column: number; readonly isInEditableRange: boolean; readonly lineContentBefore: string; - readonly wordBefore: string; - readonly wordAfter: string; constructor(model: IModel, position: IPosition, private auto: boolean) { const lineContent = model.getLineContent(position.lineNumber); @@ -47,10 +64,8 @@ export class Context { if (wordUnderCursor) { this.wordBefore = lineContent.substring(wordUnderCursor.startColumn - 1, position.column - 1); - this.wordAfter = lineContent.substring(position.column - 1, wordUnderCursor.endColumn - 1); } else { this.wordBefore = ''; - this.wordAfter = ''; } this.lineNumber = position.lineNumber; @@ -67,26 +82,6 @@ export class Context { } } - shouldAutoTrigger(): boolean { - - if (this.wordBefore.length === 0) { - // Word before position is empty - return false; - } - - if (!isNaN(Number(this.wordBefore))) { - // Word before is number only - return false; - } - - if (this.wordAfter.length > 0) { - // Word after position is non empty - return false; - } - - return true; - } - isDifferentContext(context: Context): boolean { if (this.lineNumber !== context.lineNumber) { // Line number has changed @@ -292,14 +287,12 @@ export class SuggestModel implements IDisposable { return; } - const ctx = new Context(model, this.editor.getPosition(), false); - if (this.state === State.Idle) { if (this.editor.getConfiguration().contribInfo.quickSuggestions) { // trigger suggest from idle when configured to do so this.cancel(); - if (ctx.shouldAutoTrigger()) { + if (Context.shouldAutoTrigger(this.editor)) { this.triggerAutoSuggestPromise = TPromise.timeout(this.quickSuggestDelay); this.triggerAutoSuggestPromise.then(() => { this.triggerAutoSuggestPromise = null; @@ -310,6 +303,7 @@ export class SuggestModel implements IDisposable { } else { // refine active suggestion + const ctx = new Context(model, this.editor.getPosition(), false); this.onNewContext(ctx); } } diff --git a/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts index aa331b329d3..3c70130c991 100644 --- a/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts @@ -49,8 +49,10 @@ suite('SuggestModel - Context', function () { function assertAutoTrigger(offset: number, expected: boolean): void { const pos = model.getPositionAt(offset); - const ctx = new Context(model, pos, false); - assert.equal(ctx.shouldAutoTrigger(), expected); + const editor = createMockEditor(model); + editor.setPosition(pos); + assert.equal(Context.shouldAutoTrigger(editor), expected); + editor.dispose(); } assertAutoTrigger(3, true); // end of word, Das| @@ -239,4 +241,4 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { }); }); }); -}); \ No newline at end of file +}); From 7f967892792f82ee5cc7730aed6c465218d10a01 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 4 Jan 2017 17:17:50 +0100 Subject: [PATCH 076/131] suggest - move isInEditableRange into static and reduce Context properties --- .../contrib/suggest/common/suggestModel.ts | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/suggest/common/suggestModel.ts b/src/vs/editor/contrib/suggest/common/suggestModel.ts index 08bbaeb674b..6ff69fe2e54 100644 --- a/src/vs/editor/contrib/suggest/common/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/common/suggestModel.ts @@ -51,9 +51,20 @@ export class Context { return true; } + static isInEditableRange(editor: ICommonCodeEditor): boolean { + const model = editor.getModel(); + const position = editor.getPosition(); + if (model.hasEditableRange()) { + const editableRange = model.getEditableRange(); + if (!editableRange.containsPosition(position)) { + return false; + } + } + return true; + } + readonly lineNumber: number; readonly column: number; - readonly isInEditableRange: boolean; readonly lineContentBefore: string; readonly wordBefore: string; @@ -71,15 +82,6 @@ export class Context { this.lineNumber = position.lineNumber; this.column = position.column; this.lineContentBefore = lineContent.substr(0, position.column - 1); - - this.isInEditableRange = true; - if (model.hasEditableRange()) { - const editableRange = model.getEditableRange(); - - if (!editableRange.containsPosition(position)) { - this.isInEditableRange = false; - } - } } isDifferentContext(context: Context): boolean { @@ -318,7 +320,7 @@ export class SuggestModel implements IDisposable { const ctx = new Context(model, this.editor.getPosition(), auto); - if (!ctx.isInEditableRange) { + if (!Context.isInEditableRange(this.editor)) { return; } From 03e88fd3e6736c36cf485e9e1d9ab500247d7211 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 4 Jan 2017 17:23:45 +0100 Subject: [PATCH 077/131] rename Context to LineContext, more simplifications --- .../contrib/suggest/common/suggestModel.ts | 33 +++++++------------ .../suggest/test/common/suggestModel.test.ts | 10 +++--- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/src/vs/editor/contrib/suggest/common/suggestModel.ts b/src/vs/editor/contrib/suggest/common/suggestModel.ts index 6ff69fe2e54..d5b2bbe1697 100644 --- a/src/vs/editor/contrib/suggest/common/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/common/suggestModel.ts @@ -30,7 +30,7 @@ export interface ISuggestEvent { auto: boolean; } -export class Context { +export class LineContext { static shouldAutoTrigger(editor: ICommonCodeEditor): boolean { const model = editor.getModel(); @@ -65,26 +65,17 @@ export class Context { readonly lineNumber: number; readonly column: number; - readonly lineContentBefore: string; readonly wordBefore: string; constructor(model: IModel, position: IPosition, private auto: boolean) { - const lineContent = model.getLineContent(position.lineNumber); - const wordUnderCursor = model.getWordAtPosition(position); - - if (wordUnderCursor) { - this.wordBefore = lineContent.substring(wordUnderCursor.startColumn - 1, position.column - 1); - } else { - this.wordBefore = ''; - } - + this.lineContentBefore = model.getLineContent(position.lineNumber).substr(0, position.column - 1); + this.wordBefore = model.getWordUntilPosition(position).word; this.lineNumber = position.lineNumber; this.column = position.column; - this.lineContentBefore = lineContent.substr(0, position.column - 1); } - isDifferentContext(context: Context): boolean { + isDifferentContext(context: LineContext): boolean { if (this.lineNumber !== context.lineNumber) { // Line number has changed return true; @@ -108,7 +99,7 @@ export class Context { return false; } - shouldRetrigger(context: Context): boolean { + shouldRetrigger(context: LineContext): boolean { if (!startsWith(this.lineContentBefore, context.lineContentBefore)) { // Doesn't look like the same line return false; @@ -144,7 +135,7 @@ export class SuggestModel implements IDisposable { private state: State; private requestPromise: TPromise; - private context: Context; + private context: LineContext; private completionModel: CompletionModel; @@ -294,7 +285,7 @@ export class SuggestModel implements IDisposable { if (this.editor.getConfiguration().contribInfo.quickSuggestions) { // trigger suggest from idle when configured to do so this.cancel(); - if (Context.shouldAutoTrigger(this.editor)) { + if (LineContext.shouldAutoTrigger(this.editor)) { this.triggerAutoSuggestPromise = TPromise.timeout(this.quickSuggestDelay); this.triggerAutoSuggestPromise.then(() => { this.triggerAutoSuggestPromise = null; @@ -305,7 +296,7 @@ export class SuggestModel implements IDisposable { } else { // refine active suggestion - const ctx = new Context(model, this.editor.getPosition(), false); + const ctx = new LineContext(model, this.editor.getPosition(), false); this.onNewContext(ctx); } } @@ -318,9 +309,9 @@ export class SuggestModel implements IDisposable { return; } - const ctx = new Context(model, this.editor.getPosition(), auto); + const ctx = new LineContext(model, this.editor.getPosition(), auto); - if (!Context.isInEditableRange(this.editor)) { + if (!LineContext.isInEditableRange(this.editor)) { return; } @@ -351,7 +342,7 @@ export class SuggestModel implements IDisposable { items = items.concat(existingItems).sort(cmpFn); } - const ctx = new Context(model, this.editor.getPosition(), auto); + const ctx = new LineContext(model, this.editor.getPosition(), auto); this.completionModel = new CompletionModel(items, this.context.column, { leadingLineContent: ctx.lineContentBefore, characterCountDelta: this.context ? ctx.column - this.context.column : 0 @@ -361,7 +352,7 @@ export class SuggestModel implements IDisposable { }).then(null, onUnexpectedError); } - private onNewContext(ctx: Context): void { + private onNewContext(ctx: LineContext): void { if (this.context && this.context.isDifferentContext(ctx)) { if (this.context.shouldRetrigger(ctx)) { this.trigger(this.state === State.Auto, true); diff --git a/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts index 3c70130c991..f6e14bc8b93 100644 --- a/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts @@ -12,7 +12,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { Model } from 'vs/editor/common/model/model'; import { ICommonCodeEditor, Handler } from 'vs/editor/common/editorCommon'; import { ISuggestSupport, ISuggestResult, SuggestRegistry } from 'vs/editor/common/modes'; -import { SuggestModel, Context } from 'vs/editor/contrib/suggest/common/suggestModel'; +import { SuggestModel, LineContext } from 'vs/editor/contrib/suggest/common/suggestModel'; import { MockCodeEditor, MockScopeLocation } from 'vs/editor/test/common/mocks/mockCodeEditor'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; @@ -51,7 +51,7 @@ suite('SuggestModel - Context', function () { const pos = model.getPositionAt(offset); const editor = createMockEditor(model); editor.setPosition(pos); - assert.equal(Context.shouldAutoTrigger(editor), expected); + assert.equal(LineContext.shouldAutoTrigger(editor), expected); editor.dispose(); } @@ -64,13 +64,13 @@ suite('SuggestModel - Context', function () { test('Context - isDifferentContext', function () { // different line - const ctx = new Context(model, { lineNumber: 1, column: 8 }, true); // Das Pfer|d - assert.equal(ctx.isDifferentContext(new Context(model, { lineNumber: 2, column: 1 }, true)), true); + const ctx = new LineContext(model, { lineNumber: 1, column: 8 }, true); // Das Pfer|d + assert.equal(ctx.isDifferentContext(new LineContext(model, { lineNumber: 2, column: 1 }, true)), true); function createEndContext(value: string) { const model = Model.createFromString(value); - const ctx = new Context(model, model.getPositionAt(value.length), true); // Das Pfer|d + const ctx = new LineContext(model, model.getPositionAt(value.length), true); // Das Pfer|d return ctx; } From e59e3aa573d02fed664b6b9324fa5f5bed75d3c0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 4 Jan 2017 19:00:42 +0100 Subject: [PATCH 078/131] fix #17400 --- .../contrib/suggest/common/suggestModel.ts | 130 ++++++++---------- .../suggest/test/common/suggestModel.test.ts | 67 ++++++--- 2 files changed, 101 insertions(+), 96 deletions(-) diff --git a/src/vs/editor/contrib/suggest/common/suggestModel.ts b/src/vs/editor/contrib/suggest/common/suggestModel.ts index d5b2bbe1697..7e8eab227da 100644 --- a/src/vs/editor/contrib/suggest/common/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/common/suggestModel.ts @@ -9,9 +9,8 @@ import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { forEach } from 'vs/base/common/collections'; import Event, { Emitter } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { startsWith } from 'vs/base/common/strings'; import { TPromise } from 'vs/base/common/winjs.base'; -import { ICommonCodeEditor, ICursorSelectionChangedEvent, CursorChangeReason, IModel, IPosition } from 'vs/editor/common/editorCommon'; +import { ICommonCodeEditor, ICursorSelectionChangedEvent, CursorChangeReason, IModel, IPosition, IWordAtPosition } from 'vs/editor/common/editorCommon'; import { ISuggestSupport, SuggestRegistry } from 'vs/editor/common/modes'; import { provideSuggestionItems, getSuggestionComparator, ISuggestionItem } from './suggest'; import { CompletionModel } from './completionModel'; @@ -65,57 +64,16 @@ export class LineContext { readonly lineNumber: number; readonly column: number; - readonly lineContentBefore: string; - readonly wordBefore: string; + readonly leadingLineContent: string; + readonly leadingWord: IWordAtPosition; + readonly auto; - constructor(model: IModel, position: IPosition, private auto: boolean) { - this.lineContentBefore = model.getLineContent(position.lineNumber).substr(0, position.column - 1); - this.wordBefore = model.getWordUntilPosition(position).word; + constructor(model: IModel, position: IPosition, auto: boolean) { + this.leadingLineContent = model.getLineContent(position.lineNumber).substr(0, position.column - 1); + this.leadingWord = model.getWordUntilPosition(position); this.lineNumber = position.lineNumber; this.column = position.column; - } - - isDifferentContext(context: LineContext): boolean { - if (this.lineNumber !== context.lineNumber) { - // Line number has changed - return true; - } - - if (context.column < this.column - this.wordBefore.length) { - // column went before word start - return true; - } - - if (!startsWith(context.lineContentBefore, this.lineContentBefore)) { - // Line has changed before position - return true; - } - - if (context.wordBefore === '' && context.lineContentBefore !== this.lineContentBefore) { - // Most likely a space has been typed - return true; - } - - return false; - } - - shouldRetrigger(context: LineContext): boolean { - if (!startsWith(this.lineContentBefore, context.lineContentBefore)) { - // Doesn't look like the same line - return false; - } - - if (this.lineContentBefore.length > context.lineContentBefore.length && this.wordBefore.length === 0) { - // Text was deleted and previous current word was empty - return false; - } - - if (this.auto && context.wordBefore.length === 0) { - // Currently in auto mode and new current word is empty - return false; - } - - return true; + this.auto = auto; } } @@ -283,7 +241,7 @@ export class SuggestModel implements IDisposable { if (this.state === State.Idle) { if (this.editor.getConfiguration().contribInfo.quickSuggestions) { - // trigger suggest from idle when configured to do so + // trigger 24x7 IntelliSense when idle and enabled this.cancel(); if (LineContext.shouldAutoTrigger(this.editor)) { this.triggerAutoSuggestPromise = TPromise.timeout(this.quickSuggestDelay); @@ -296,7 +254,7 @@ export class SuggestModel implements IDisposable { } else { // refine active suggestion - const ctx = new LineContext(model, this.editor.getPosition(), false); + const ctx = new LineContext(model, this.editor.getPosition(), this.state === State.Auto); this.onNewContext(ctx); } } @@ -344,7 +302,7 @@ export class SuggestModel implements IDisposable { const ctx = new LineContext(model, this.editor.getPosition(), auto); this.completionModel = new CompletionModel(items, this.context.column, { - leadingLineContent: ctx.lineContentBefore, + leadingLineContent: ctx.leadingLineContent, characterCountDelta: this.context ? ctx.column - this.context.column : 0 }); this.onNewContext(ctx); @@ -353,41 +311,65 @@ export class SuggestModel implements IDisposable { } private onNewContext(ctx: LineContext): void { - if (this.context && this.context.isDifferentContext(ctx)) { - if (this.context.shouldRetrigger(ctx)) { - this.trigger(this.state === State.Auto, true); - } else { - this.cancel(); + + if (!this.context) { + // happens when 24x7 IntelliSense is enabled and still in its delay + return; + } + + if (ctx.lineNumber !== this.context.lineNumber) { + // e.g. happens when pressing Enter while IntelliSense is computed + this.cancel(); + return; + } + + if (ctx.column < this.context.column) { + // typed -> moved cursor LEFT -> retrigger if still on a word + if (ctx.leadingWord.word) { + this.trigger(this.context.auto, true); } + return; + } - } else if (this.completionModel) { + if (!this.completionModel) { + // happens when IntelliSense is not yet computed + return; + } - if (this.completionModel.incomplete && ctx.column > this.context.column) { - const {complete, incomplete} = this.completionModel.resolveIncompleteInfo(); - this.trigger(this.state === State.Auto, true, incomplete, complete); - return; - } + if (ctx.column > this.context.column && this.completionModel.incomplete) { + // typed -> moved cursor RIGHT & incomple model -> retrigger + const {complete, incomplete} = this.completionModel.resolveIncompleteInfo(); + this.trigger(this.state === State.Auto, true, incomplete, complete); - const auto = this.state === State.Auto; - const oldLineContext = this.completionModel.lineContext; + } else { + // typed -> moved cursor RIGHT -> update UI + let oldLineContext = this.completionModel.lineContext; let isFrozen = false; this.completionModel.lineContext = { - leadingLineContent: ctx.lineContentBefore, - characterCountDelta: this.context ? ctx.column - this.context.column : 0 + leadingLineContent: ctx.leadingLineContent, + characterCountDelta: ctx.column - this.context.column }; - // when explicitly request when the next context goes - // from 'results' to 'no results' freeze - if (!auto && this.completionModel.items.length === 0) { - this.completionModel.lineContext = oldLineContext; - isFrozen = this.completionModel.items.length > 0; + if (this.completionModel.items.length === 0) { + + if (LineContext.shouldAutoTrigger(this.editor) && this.context.leadingWord.endColumn < ctx.leadingWord.startColumn) { + // retrigger when heading into a new word + this.trigger(this.context.auto, true); + return; + } + + if (!this.context.auto) { + // freeze when IntelliSense was manually requested + this.completionModel.lineContext = oldLineContext; + isFrozen = this.completionModel.items.length > 0; + } } this._onDidSuggest.fire({ completionModel: this.completionModel, + auto: this.context.auto, isFrozen, - auto }); } } diff --git a/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts index f6e14bc8b93..7304fc44d36 100644 --- a/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts @@ -61,28 +61,6 @@ suite('SuggestModel - Context', function () { assertAutoTrigger(55, false); // number, 1861| }); - test('Context - isDifferentContext', function () { - - // different line - const ctx = new LineContext(model, { lineNumber: 1, column: 8 }, true); // Das Pfer|d - assert.equal(ctx.isDifferentContext(new LineContext(model, { lineNumber: 2, column: 1 }, true)), true); - - - function createEndContext(value: string) { - const model = Model.createFromString(value); - const ctx = new LineContext(model, model.getPositionAt(value.length), true); // Das Pfer|d - return ctx; - } - - // got shorter -> redo - assert.equal(createEndContext('One Two').isDifferentContext(createEndContext('One Tw')), true); - - // got longer inside word -> keep - assert.equal(createEndContext('One Tw').isDifferentContext(createEndContext('One Two')), false); - - // got longer new word -> redo - assert.equal(createEndContext('One Two').isDifferentContext(createEndContext('One Two ')), true); - }); }); suite('SuggestModel - TriggerAndCancelOracle', function () { @@ -241,4 +219,49 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { }); }); }); + + test('#17400: Keep filtering suggestModel.ts after space', function () { + + disposables.push(SuggestRegistry.register({ scheme: 'test' }, { + triggerCharacters: [], + provideCompletionItems(doc, pos) { + return { + currentWord: '', + incomplete: false, + suggestions: [{ + label: 'My Table', + type: 'property', + insertText: 'My Table' + }] + }; + } + })); + + model.setValue(''); + + return withOracle((model, editor) => { + + return assertEvent(model.onDidSuggest, () => { + editor.setPosition({ lineNumber: 1, column: 1 }); + editor.trigger('keyboard', Handler.Type, { text: 'My' }); + + }, event => { + assert.equal(event.auto, true); + assert.equal(event.completionModel.items.length, 1); + const [first] = event.completionModel.items; + assert.equal(first.suggestion.label, 'My Table'); + + return assertEvent(model.onDidSuggest, () => { + editor.setPosition({ lineNumber: 1, column: 3 }); + editor.trigger('keyboard', Handler.Type, { text: ' ' }); + + }, event => { + assert.equal(event.auto, true); + assert.equal(event.completionModel.items.length, 1); + const [first] = event.completionModel.items; + assert.equal(first.suggestion.label, 'My Table'); + }); + }); + }); + }); }); From f5df40a199c1998c1f9ec3dce9e70542b0a555c8 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 4 Jan 2017 10:54:44 -0800 Subject: [PATCH 079/131] Make LAUNCHING_DURATION const --- .../parts/terminal/electron-browser/terminalInstance.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 07181e043b5..2e56d1240c9 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -25,9 +25,10 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { TabFocus } from 'vs/editor/common/config/commonEditorConfig'; import { TerminalConfigHelper } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper'; +/** The amount of time to consider terminal errors to be related to the launch */ +const LAUNCHING_DURATION = 500; + export class TerminalInstance implements ITerminalInstance { - /** The amount of time to consider terminal errors to be related to the launch */ - private static readonly LAUNCHING_DURATION = 500; private static readonly EOL_REGEX = /\r?\n/g; private static _idCounter = 1; @@ -371,7 +372,7 @@ export class TerminalInstance implements ITerminalInstance { }); setTimeout(() => { this._isLaunching = false; - }, TerminalInstance.LAUNCHING_DURATION); + }, LAUNCHING_DURATION); } // TODO: This should be private/protected From 27807e35a28c21ca5fd9f5e26e092a3b7765fc36 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 4 Jan 2017 20:33:35 +0100 Subject: [PATCH 080/131] startDebug action picks first configuration if none picked by user so far fixes #18017 --- .../debug/electron-browser/debug.contribution.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts index 0f9d61b4827..828364cbc40 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts @@ -133,7 +133,16 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0), handler(accessor: ServicesAccessor, configurationOrName: any) { const debugService = accessor.get(IDebugService); - return debugService.createProcess(configurationOrName || debugService.getViewModel().selectedConfigurationName); + if (!configurationOrName) { + const viewModel = debugService.getViewModel(); + if (!viewModel.selectedConfigurationName) { + const name = debugService.getConfigurationManager().getConfigurationNames().shift(); + viewModel.setSelectedConfigurationName(name); + } + configurationOrName = viewModel.selectedConfigurationName; + } + + return debugService.createProcess(configurationOrName); }, when: CONTEXT_NOT_IN_DEBUG_MODE, primary: undefined From a2ae9109e58e186e4c2dbd323565886f92b963ff Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 4 Jan 2017 11:44:07 -0800 Subject: [PATCH 081/131] Fix markdown syntax highlighting for script or style html elements with blank lines (#18116) Fixes #18069 **Bug** Script and style blocks inside markdown cannot contain blank lines **fix** Add a specific rule for script, style, and pre blocks according to the common mark spec: http://spec.commonmark.org/0.27/#html-block --- .../markdown/syntaxes/markdown.tmLanguage | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/extensions/markdown/syntaxes/markdown.tmLanguage b/extensions/markdown/syntaxes/markdown.tmLanguage index 031589c965a..5839acf6301 100644 --- a/extensions/markdown/syntaxes/markdown.tmLanguage +++ b/extensions/markdown/syntaxes/markdown.tmLanguage @@ -320,7 +320,29 @@
begin - (^|\G)\s*(?=</?(address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h1|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|pre|p|param|script|section|source|style|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)(\s|$|/?>)) + (^|\G)\s*(?=<(script|style|pre)(\s|$|>)) + patterns + + + begin + (\s*|$) + patterns + + + include + text.html.basic + + + while + ^\s*(?!</(script|style|pre)>) + + + end + (?=</(script|style|pre)>) + + + begin + (^|\G)\s*(?=</?(address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h1|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)(\s|$|/?>)) patterns @@ -546,15 +568,6 @@ (^|\G)(?!\s*$|#|[ ]{0,3}((([*_][ ]{0,2}\2){2,}[ \t]*$\n?)|([*+-]([ ]{1,3}|\t)))|\s*\[.+?\]:|>) - - - - - - - - - fenced_code_block_css begin @@ -589,7 +602,6 @@ punctuation.definition.markdown - patterns @@ -641,7 +653,6 @@ punctuation.definition.markdown - patterns From de9ba120d9d043dfd4903885fc46a66617288d5b Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 4 Jan 2017 20:48:24 +0100 Subject: [PATCH 082/131] "Copy stack trace" -> include full path fixes #18113 --- src/vs/workbench/parts/debug/common/debugModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index 684445fd374..0562753bed2 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -349,7 +349,7 @@ export class StackFrame implements debug.IStackFrame { } public toString(): string { - return `${this.name} (${this.source.name}:${this.lineNumber})`; + return `${this.name} (${this.source.inMemory ? this.source.name : this.source.uri.fsPath}:${this.lineNumber})`; } } From c4347fce43229b954c9c5e0088e4d24b1aa0825a Mon Sep 17 00:00:00 2001 From: Charles Pierce Date: Thu, 22 Dec 2016 17:17:50 -0800 Subject: [PATCH 083/131] #14464 Preserve editor size when switching layout --- .../parts/editor/editorGroupsControl.ts | 60 +++++++++++++------ .../services/group/common/groupService.ts | 3 +- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts b/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts index ce92c4c1a14..afa47d7ebac 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts @@ -758,7 +758,7 @@ export class EditorGroupsControl implements IEditorGroupsControl, IVerticalSashL this.sashTwo.setOrientation(this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL); // Trigger layout - this.arrangeGroups(GroupArrangement.EVEN); + this.arrangeGroups(GroupArrangement.KEEP_RATIO); } } @@ -778,27 +778,49 @@ export class EditorGroupsControl implements IEditorGroupsControl, IVerticalSashL return; // need more editors } - // Minimize Others - if (arrangement === GroupArrangement.MINIMIZE_OTHERS) { - POSITIONS.forEach(position => { - if (this.visibleEditors[position]) { - if (position !== this.lastActivePosition) { - this.silosSize[position] = this.minSize; - availableSize -= this.minSize; + switch (arrangement) { + case GroupArrangement.MINIMIZE_OTHERS: + // Minimize Others + POSITIONS.forEach(position => { + if (this.visibleEditors[position]) { + if (position !== this.lastActivePosition) { + this.silosSize[position] = this.minSize; + availableSize -= this.minSize; + } } - } - }); + }); - this.silosSize[this.lastActivePosition] = availableSize; - } + this.silosSize[this.lastActivePosition] = availableSize; + break; + case GroupArrangement.EVEN: + // Even Sizes + POSITIONS.forEach(position => { + if (this.visibleEditors[position]) { + this.silosSize[position] = availableSize / visibleEditors; + } + }); + break; + case GroupArrangement.KEEP_RATIO: + // Minimized editors should remain minimized, others should keep their relative Sizes + let oldNonMinimizedTotal = 0; + POSITIONS.forEach(position => { + if (this.visibleEditors[position]) { + if (this.silosMinimized[position]) { + this.silosSize[position] = this.minSize; + availableSize -= this.minSize; + } else { + oldNonMinimizedTotal += this.silosSize[position]; + } + } + }); - // Even Sizes - else if (arrangement === GroupArrangement.EVEN) { - POSITIONS.forEach(position => { - if (this.visibleEditors[position]) { - this.silosSize[position] = availableSize / visibleEditors; - } - }); + // Set size for non-minimized editors + const scaleFactor = availableSize / oldNonMinimizedTotal; + POSITIONS.forEach(position => { + if (this.visibleEditors[position] && !this.silosMinimized[position]) { + this.silosSize[position] *= scaleFactor; + } + }); } // Since we triggered a change in minimized/maximized editors, we need diff --git a/src/vs/workbench/services/group/common/groupService.ts b/src/vs/workbench/services/group/common/groupService.ts index 31829a11901..b27ab709b67 100644 --- a/src/vs/workbench/services/group/common/groupService.ts +++ b/src/vs/workbench/services/group/common/groupService.ts @@ -12,7 +12,8 @@ import Event from 'vs/base/common/event'; export enum GroupArrangement { MINIMIZE_OTHERS, - EVEN + EVEN, + KEEP_RATIO } export type GroupOrientation = 'vertical' | 'horizontal'; From 38038ba9c71aaa5b0e0f4228d257ceb36f908237 Mon Sep 17 00:00:00 2001 From: Charles Pierce Date: Wed, 4 Jan 2017 07:13:35 -0800 Subject: [PATCH 084/131] #14464 Remove KEEP_RATIO from the exposed API --- .../workbench/browser/parts/editor/editorGroupsControl.ts | 6 +++--- src/vs/workbench/services/group/common/groupService.ts | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts b/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts index afa47d7ebac..ebf4a30612c 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts @@ -758,7 +758,7 @@ export class EditorGroupsControl implements IEditorGroupsControl, IVerticalSashL this.sashTwo.setOrientation(this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL); // Trigger layout - this.arrangeGroups(GroupArrangement.KEEP_RATIO); + this.arrangeGroups(); } } @@ -766,7 +766,7 @@ export class EditorGroupsControl implements IEditorGroupsControl, IVerticalSashL return this.layoutVertically ? 'vertical' : 'horizontal'; } - public arrangeGroups(arrangement: GroupArrangement): void { + public arrangeGroups(arrangement?: GroupArrangement): void { if (!this.dimension) { return; // too early } @@ -800,7 +800,7 @@ export class EditorGroupsControl implements IEditorGroupsControl, IVerticalSashL } }); break; - case GroupArrangement.KEEP_RATIO: + default: // Minimized editors should remain minimized, others should keep their relative Sizes let oldNonMinimizedTotal = 0; POSITIONS.forEach(position => { diff --git a/src/vs/workbench/services/group/common/groupService.ts b/src/vs/workbench/services/group/common/groupService.ts index b27ab709b67..31829a11901 100644 --- a/src/vs/workbench/services/group/common/groupService.ts +++ b/src/vs/workbench/services/group/common/groupService.ts @@ -12,8 +12,7 @@ import Event from 'vs/base/common/event'; export enum GroupArrangement { MINIMIZE_OTHERS, - EVEN, - KEEP_RATIO + EVEN } export type GroupOrientation = 'vertical' | 'horizontal'; From 80481edbdf1272a87ca54d4c48a829e0a134dffe Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 4 Jan 2017 15:43:52 -0800 Subject: [PATCH 085/131] Remove logging statement --- extensions/typescript/src/features/completionItemProvider.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/typescript/src/features/completionItemProvider.ts b/extensions/typescript/src/features/completionItemProvider.ts index 65a725816fa..1894dab23ba 100644 --- a/extensions/typescript/src/features/completionItemProvider.ts +++ b/extensions/typescript/src/features/completionItemProvider.ts @@ -220,7 +220,6 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP // Don't complete function calls inside of destructive assigments or imports return this.client.execute('quickinfo', args).then(infoResponse => { const info = infoResponse.body; - console.log(info && info.kind); switch (info && info.kind) { case 'var': case 'let': From 023c83fe4d3fd216bc63b73a4822a02557119d54 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 5 Jan 2017 08:39:44 +0100 Subject: [PATCH 086/131] update distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d099f65135a..c6c21afc8cf 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "code-oss-dev", "version": "1.9.0", "electronVersion": "1.4.6", - "distro": "2eecc8b68318fba1fc5b62930287914ac9225e8a", + "distro": "ef07477c3bbf2aa2f274b13093cbe0d96fa59fdd", "author": { "name": "Microsoft Corporation" }, @@ -116,4 +116,4 @@ "windows-mutex": "^0.2.0", "fsevents": "0.3.8" } -} +} \ No newline at end of file From 5340c2301f6812c2d9117ed6b34bfa881d321981 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 5 Jan 2017 08:48:47 +0100 Subject: [PATCH 087/131] :lipstick: --- src/vs/code/electron-main/window.ts | 34 ++++++++++++++++------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index d5c0da3e542..56536188d21 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -205,21 +205,6 @@ export class VSCodeWindow implements IVSCodeWindow { this._win = new BrowserWindow(options); this._id = this._win.id; - // TODO@joao: hook this up to some initialization routine - // this causes a race between setting the headers and doing - // a request that needs them. chances are low - getCommonHTTPHeaders().done(headers => { - if (!this._win) { - return; - } - - const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*']; - - this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => { - cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) }); - }); - }); - if (isFullscreenOrMaximized) { this.win.maximize(); @@ -238,9 +223,28 @@ export class VSCodeWindow implements IVSCodeWindow { this.setMenuBarVisibility(false); // respect configured menu bar visibility } + // TODO@joao: hook this up to some initialization routine + // this causes a race between setting the headers and doing + // a request that needs them. chances are low + this.setCommonHTTPHeaders(); + this.registerListeners(); } + private setCommonHTTPHeaders(): void { + getCommonHTTPHeaders().done(headers => { + if (!this._win) { + return; + } + + const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*']; + + this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => { + cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) }); + }); + }); + } + public hasHiddenTitleBarStyle(): boolean { return this.hiddenTitleBarStyle; } From 4816c4a3177f74283d31952b2935aaa641f45ffa Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 5 Jan 2017 08:52:12 +0100 Subject: [PATCH 088/131] fix broken picker --- .../workbench/browser/parts/quickopen/quickOpenController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index c99ab5c20ac..e1acf4872ec 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -358,9 +358,9 @@ export class QuickOpenController extends WorkbenchComponent implements IQuickOpe let index = -1; let context: IEntryRunContext; - entries.forEach((entry, i) => { + entries.forEach(entry => { if (entry.shouldRunWithContext) { - index = i; + index = entry.index; context = entry.shouldRunWithContext; } }); From 4496c87db5517e1b268b1040163155a625c7f5da Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 5 Jan 2017 10:22:14 +0100 Subject: [PATCH 089/131] Allow to compare untitled documents (fixes #14501) --- .../parts/files/browser/fileActions.ts | 22 +++++++++++-------- .../files/browser/views/openEditorsViewer.ts | 21 +++++++++--------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts index 94b082fb8ff..8006157ba6c 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -1119,11 +1119,11 @@ export class GlobalCompareResourcesAction extends Action { } public run(): TPromise { - const fileResource = toResource(this.editorService.getActiveEditorInput(), { filter: 'file' }); - if (fileResource) { + const activeResource = toResource(this.editorService.getActiveEditorInput(), { filter: ['file', 'untitled'] }); + if (activeResource) { // Keep as resource to compare - globalResourceToCompare = fileResource; + globalResourceToCompare = activeResource; // Pick another entry from history interface IHistoryPickEntry extends IFilePickOpenEntry { @@ -1137,18 +1137,22 @@ export class GlobalCompareResourcesAction extends Action { let description: string; if (input instanceof EditorInput) { - return void 0; // only files supported + resource = toResource(input, { filter: ['file', 'untitled'] }); + } else { + resource = (input as IResourceInput).resource; } - const resourceInput = input as IResourceInput; - resource = resourceInput.resource; - label = paths.basename(resourceInput.resource.fsPath); - description = labels.getPathLabel(paths.dirname(resource.fsPath), this.contextService); + if (!resource) { + return void 0; // only support to compare with files and untitled + } + + label = paths.basename(resource.fsPath); + description = resource.scheme === 'file' ? labels.getPathLabel(paths.dirname(resource.fsPath), this.contextService) : void 0; return { input, resource, label, description }; }).filter(p => !!p); - return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickHistory', "Select an editor history entry to compare with"), autoFocus: { autoFocusFirstEntry: true }, matchOnDescription: true }).then(pick => { + return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickHistory', "Select a previously opened file to compare with"), autoFocus: { autoFocusFirstEntry: true }, matchOnDescription: true }).then(pick => { if (pick) { const compareAction = this.instantiationService.createInstance(CompareResourcesAction, pick.resource, null); if (compareAction._isEnabled()) { diff --git a/src/vs/workbench/parts/files/browser/views/openEditorsViewer.ts b/src/vs/workbench/parts/files/browser/views/openEditorsViewer.ts index 7b9579c7f95..99b26caf5ef 100644 --- a/src/vs/workbench/parts/files/browser/views/openEditorsViewer.ts +++ b/src/vs/workbench/parts/files/browser/views/openEditorsViewer.ts @@ -413,6 +413,7 @@ export class ActionProvider extends ContributableActionProvider { result.unshift(this.instantiationService.createInstance(OpenToSideAction, tree, resource, false)); if (!openEditor.isUntitled()) { + // Files: Save / Revert if (!autoSaveEnabled) { result.push(new Separator()); @@ -427,18 +428,10 @@ export class ActionProvider extends ContributableActionProvider { revertAction.enabled = openEditor.isDirty(); result.push(revertAction); } - - result.push(new Separator()); - - // Compare Actions - const runCompareAction = this.instantiationService.createInstance(CompareResourcesAction, resource, tree); - if (runCompareAction._isEnabled()) { - result.push(runCompareAction); - } - result.push(this.instantiationService.createInstance(SelectResourceForCompareAction, resource, tree)); } + // Untitled: Save / Save As - else { + if (openEditor.isUntitled()) { result.push(new Separator()); if (this.untitledEditorService.hasAssociatedFilePath(resource)) { @@ -452,6 +445,14 @@ export class ActionProvider extends ContributableActionProvider { result.push(saveAsAction); } + // Compare Actions + result.push(new Separator()); + const runCompareAction = this.instantiationService.createInstance(CompareResourcesAction, resource, tree); + if (runCompareAction._isEnabled()) { + result.push(runCompareAction); + } + result.push(this.instantiationService.createInstance(SelectResourceForCompareAction, resource, tree)); + result.push(new Separator()); } From 6c75921f2a7a3a2d976d1a71490c79f84f3b023b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 5 Jan 2017 12:45:23 +0100 Subject: [PATCH 090/131] remove process.env warning --- .../electron-browser/bootstrap/index.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/vs/workbench/electron-browser/bootstrap/index.js b/src/vs/workbench/electron-browser/bootstrap/index.js index b03d213e348..b7c60e8f6c2 100644 --- a/src/vs/workbench/electron-browser/bootstrap/index.js +++ b/src/vs/workbench/electron-browser/bootstrap/index.js @@ -16,24 +16,7 @@ const ipc = electron.ipcRenderer; process.lazyEnv = new Promise(function (resolve) { - - const origEnv = process.env; - - // warn about missing environment variables - // while we are resolve lazyEnv - process.env = new Proxy(origEnv, { - get: function (target, name) { - const result = target[name]; - if (typeof result === 'undefined') { - console.warn('process.env[\'' + name + '\'] is undefined AND \'process.lazyEnv\' is NOT READY yet.'); - } - return result; - } - }); - ipc.once('vscode:acceptShellEnv', function (event, shellEnv) { - // store process.env, mixin shellEnv, done - process.env = origEnv; assign(process.env, shellEnv); resolve(process.env); }); From 680725f0fd6eb6035f123720f3b9e0ea790af06f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 5 Jan 2017 15:03:58 +0100 Subject: [PATCH 091/131] first part of #10795 --- src/vs/platform/editor/common/editor.ts | 6 ++++++ src/vs/workbench/common/editor.ts | 6 +++++- src/vs/workbench/parts/files/browser/fileActions.ts | 10 +++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/editor/common/editor.ts b/src/vs/platform/editor/common/editor.ts index d789272604a..ea5a3ee80fe 100644 --- a/src/vs/platform/editor/common/editor.ts +++ b/src/vs/platform/editor/common/editor.ts @@ -9,6 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import Event from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { IEditorViewState } from 'vs/editor/common/editorCommon'; export const IEditorService = createDecorator('editorService'); @@ -238,6 +239,11 @@ export interface ITextEditorOptions extends IEditorOptions { endColumn?: number; }; + /** + * Text editor view state. + */ + viewState?: IEditorViewState; + /** * Option to scroll vertically or horizontally as necessary and reveal a range centered vertically only if it lies outside the viewport. */ diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 4717f2ec35c..dc18cc01c1f 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -583,7 +583,7 @@ export class TextEditorOptions extends EditorOptions { public static from(input: IBaseResourceInput): TextEditorOptions { let options: TextEditorOptions = null; if (input && input.options) { - if (input.options.selection || input.options.forceOpen || input.options.revealIfVisible || input.options.preserveFocus || input.options.pinned || input.options.inactive || typeof input.options.index === 'number') { + if (input.options.selection || input.options.viewState || input.options.forceOpen || input.options.revealIfVisible || input.options.preserveFocus || input.options.pinned || input.options.inactive || typeof input.options.index === 'number') { options = new TextEditorOptions(); } @@ -616,6 +616,10 @@ export class TextEditorOptions extends EditorOptions { options.revealInCenterIfOutsideViewport = true; } + if (input.options.viewState) { + options.editorViewState = input.options.viewState; + } + if (typeof input.options.index === 'number') { options.index = input.options.index; } diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts index 8006157ba6c..623df36d1cb 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -44,8 +44,8 @@ import { IInstantiationService, IConstructorSignature2 } from 'vs/platform/insta import { IMessageService, IMessageWithAction, IConfirmation, Severity, CancelAction } from 'vs/platform/message/common/message'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { Keybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { Selection } from 'vs/editor/common/core/selection'; import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; +import { IEditorViewState } from 'vs/editor/common/editorCommon'; export interface IEditableData { action: IAction; @@ -1307,13 +1307,13 @@ export abstract class BaseSaveFileAction extends BaseActionWithErrorReporting { encodingOfSource = textModel && textModel.getEncoding(); // text model can be null e.g. if this is a binary file! } - let selectionOfSource: Selection; + let viewStateOfSource: IEditorViewState; const activeEditor = this.editorService.getActiveEditor(); const editor = getCodeEditor(activeEditor); if (editor) { const activeResource = toResource(activeEditor.input, { supportSideBySide: true, filter: ['file', 'untitled'] }); if (activeResource && activeResource.toString() === source.toString()) { - selectionOfSource = editor.getSelection(); + viewStateOfSource = editor.saveViewState(); } } @@ -1344,13 +1344,13 @@ export abstract class BaseSaveFileAction extends BaseActionWithErrorReporting { encoding: encodingOfSource, options: { pinned: true, - selection: selectionOfSource + viewState: viewStateOfSource } }; return this.editorService.replaceEditors([{ toReplace: { resource: source }, - replaceWith: replaceWith + replaceWith }]).then(() => true); }); } From 61ef2f384404f22c70451875fcb03438caea3e56 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 5 Jan 2017 15:12:49 +0100 Subject: [PATCH 092/131] debug: show origin in editor header fixes #18148 --- .../workbench/parts/debug/browser/debugActions.ts | 10 +--------- src/vs/workbench/parts/debug/common/debug.ts | 2 ++ src/vs/workbench/parts/debug/common/debugModel.ts | 14 ++++++++++++++ .../parts/debug/electron-browser/debugService.ts | 9 +-------- .../parts/debug/electron-browser/debugViewer.ts | 10 +--------- 5 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts index 2f2f422e136..997807ba6e9 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -717,15 +717,7 @@ export class FocusProcessAction extends AbstractDebugAction { return this.debugService.focusStackFrameAndEvaluate(null, process).then(() => { const stackFrame = this.debugService.getViewModel().focusedStackFrame; if (stackFrame) { - return this.editorService.openEditor({ - resource: stackFrame.source.uri, - options: { - preserveFocus: true, - selection: { startLineNumber: stackFrame.lineNumber, startColumn: 1 }, - revealIfVisible: true, - revealInCenterIfOutsideViewport: true - } - }); + return stackFrame.openInEditor(this.editorService, true); } }); } diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index 86188d6eb8d..c665252b3e5 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -14,6 +14,7 @@ import { ISuggestion } from 'vs/editor/common/modes'; import { Source } from 'vs/workbench/parts/debug/common/debugSource'; import { Range } from 'vs/editor/common/core/range'; import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; export const VIEWLET_ID = 'workbench.view.debug'; export const REPL_ID = 'workbench.panel.repl'; @@ -170,6 +171,7 @@ export interface IStackFrame extends ITreeElement { getScopes(): TPromise; restart(): TPromise; toString(): string; + openInEditor(editorService: IWorkbenchEditorService, preserveFocus?: boolean, sideBySide?: boolean): TPromise; } export interface IEnablement extends ITreeElement { diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index 0562753bed2..68921520b50 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -19,6 +19,7 @@ import { ISuggestion } from 'vs/editor/common/modes'; import { Position } from 'vs/editor/common/core/position'; import * as debug from 'vs/workbench/parts/debug/common/debug'; import { Source } from 'vs/workbench/parts/debug/common/debugSource'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; const MAX_REPL_LENGTH = 10000; const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source"); @@ -351,6 +352,19 @@ export class StackFrame implements debug.IStackFrame { public toString(): string { return `${this.name} (${this.source.inMemory ? this.source.name : this.source.uri.fsPath}:${this.lineNumber})`; } + + public openInEditor(editorService: IWorkbenchEditorService, preserveFocus?: boolean, sideBySide?: boolean): TPromise { + return editorService.openEditor({ + resource: this.source.uri, + description: this.source.origin, + options: { + preserveFocus, + selection: { startLineNumber: this.lineNumber, startColumn: 1 }, + revealIfVisible: true, + revealInCenterIfOutsideViewport: true + } + }, sideBySide); + } } export class Thread implements debug.IThread { diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index ad7f1883698..910bbd3b8bb 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -283,14 +283,7 @@ export class DebugService implements debug.IDebugService { this.windowService.getWindow().focus(); aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", event.body.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.lineNumber)); - return this.editorService.openEditor({ - resource: stackFrameToFocus.source.uri, - options: { - selection: { startLineNumber: stackFrameToFocus.lineNumber, startColumn: 1 }, - revealIfVisible: true, - revealInCenterIfOutsideViewport: true - } - }); + return stackFrameToFocus.openInEditor(this.editorService); } }); } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts index c8e9b640f37..576bba7a893 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts @@ -309,15 +309,7 @@ export class CallStackController extends BaseDebugController { private focusStackFrame(stackFrame: debug.IStackFrame, event: IKeyboardEvent | IMouseEvent, preserveFocus: boolean): void { this.debugService.focusStackFrameAndEvaluate(stackFrame).then(() => { const sideBySide = (event && (event.ctrlKey || event.metaKey)); - return this.editorService.openEditor({ - resource: stackFrame.source.uri, - options: { - preserveFocus, - selection: { startLineNumber: stackFrame.lineNumber, startColumn: 1 }, - revealIfVisible: true, - revealInCenterIfOutsideViewport: true - }, - }, sideBySide); + return stackFrame.openInEditor(this.editorService, preserveFocus, sideBySide); }, errors.onUnexpectedError); } } From 0d27e814eb8f2f39438c95d2826d04c9fbd61f4f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 5 Jan 2017 15:31:33 +0100 Subject: [PATCH 093/131] fix bad regression when doing "Save as" --- src/vs/workbench/services/textfile/common/textFileService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 9da33446228..d622ec34313 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -562,7 +562,7 @@ export abstract class TextFileService implements ITextFileService { private doSaveTextFileAs(sourceModel: ITextFileEditorModel | UntitledEditorModel, resource: URI, target: URI): TPromise { // create the target file empty if it does not exist already - return this.fileService.resolveFile(target).then(stat => stat, () => null).then(stat => stat || this.fileService.createFile(target)).then(stat => { + return this.fileService.resolveFile(target).then(stat => stat, () => null).then(stat => stat || this.fileService.updateContent(target, '')).then(stat => { // resolve a model for the file (which can be binary if the file is not a text file) return this.models.loadOrCreate(target).then((targetModel: ITextFileEditorModel) => { From 0642999457de9f16546b433dbdd83100c5271fe2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 5 Jan 2017 06:52:30 -0800 Subject: [PATCH 094/131] Add null check Fixes #18159 --- .../parts/terminal/electron-browser/terminalActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index c0949423672..4aa82872e94 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -227,7 +227,7 @@ export class SwitchTerminalInstanceAction extends Action { } public run(item?: string): TPromise { - if (!item) { + if (!item || !item.split) { return TPromise.as(null); } const selectedTerminalIndex = parseInt(item.split(':')[0], 10) - 1; From da493459dfb64ad08255d8ed19abf15b8fce7f97 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 5 Jan 2017 15:53:39 +0100 Subject: [PATCH 095/131] fix bad hover title for debug internal module --- src/vs/workbench/browser/labels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index dfbd18ee310..ba99b7663df 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -110,7 +110,7 @@ export class ResourceLabel extends IconLabel { const resource = this.label.resource; let title = ''; - if (this.options && this.options.title) { + if (this.options && typeof this.options.title === 'string') { title = this.options.title; } else if (resource) { title = getPathLabel(resource.fsPath); From 019ce0eb56e03d07d50148385e87f28eb1d00e9e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 5 Jan 2017 16:17:29 +0100 Subject: [PATCH 096/131] fix #18160 --- .../contrib/goToDeclaration/browser/goToDeclaration.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts index 979421d8f9d..10e0304f3a2 100644 --- a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts +++ b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts @@ -76,7 +76,7 @@ export class DefinitionAction extends EditorAction { let result: Location[] = []; for (let i = 0; i < references.length; i++) { let reference = references[i]; - if (!reference) { + if (!reference || !reference.range) { continue; } let {uri, range} = reference; @@ -110,7 +110,7 @@ export class DefinitionAction extends EditorAction { } else { let next = model.nearestReference(editor.getModel().uri, editor.getPosition()); this._openReference(editorService, next, this._configuration.openToSide).then(editor => { - if (model.references.length > 1) { + if (editor && model.references.length > 1) { this._openInPeek(editorService, editor, model); } else { model.dispose(); @@ -128,7 +128,7 @@ export class DefinitionAction extends EditorAction { revealIfVisible: !sideBySide } }, sideBySide).then(editor => { - return editor.getControl(); + return editor && editor.getControl(); }); } From 94f30c3e8b3fcf78ca56c8b33ab115d4a71e131c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 5 Jan 2017 10:10:24 -0800 Subject: [PATCH 097/131] Add --new-window-if-not-first CLI arg This allows shift+click/middle+click/num+super Windows and Unity shortcuts to work as expected while not changing regular launch behavior. Part of #48 --- resources/linux/code.desktop | 2 +- src/vs/code/electron-main/launch.ts | 4 ++-- src/vs/code/electron-main/main.ts | 2 +- src/vs/platform/environment/common/environment.ts | 1 + src/vs/platform/environment/node/argv.ts | 1 + 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/resources/linux/code.desktop b/resources/linux/code.desktop index 80d41601b7b..47ed1616faa 100644 --- a/resources/linux/code.desktop +++ b/resources/linux/code.desktop @@ -2,7 +2,7 @@ Name=@@NAME_LONG@@ Comment=Code Editing. Redefined. GenericName=Text Editor -Exec=/usr/share/@@NAME@@/@@NAME@@ %U +Exec=/usr/share/@@NAME@@/@@NAME@@ --new-window-if-not-first %U Icon=@@NAME@@ Type=Application StartupNotify=true diff --git a/src/vs/code/electron-main/launch.ts b/src/vs/code/electron-main/launch.ts index 026fad8e55b..e413e7775bf 100644 --- a/src/vs/code/electron-main/launch.ts +++ b/src/vs/code/electron-main/launch.ts @@ -93,7 +93,7 @@ export class LaunchService implements ILaunchService { let usedWindows: VSCodeWindow[]; if (!!args.extensionDevelopmentPath) { this.windowsService.openExtensionDevelopmentHostWindow({ context, cli: args, userEnv }); - } else if (args._.length === 0 && args['new-window']) { + } else if (args._.length === 0 && args['new-window'] || args['new-window-if-not-first']) { usedWindows = this.windowsService.open({ context, cli: args, userEnv, forceNewWindow: true, forceEmpty: true }); } else if (args._.length === 0) { usedWindows = [this.windowsService.focusLastActive(args, context)]; @@ -102,7 +102,7 @@ export class LaunchService implements ILaunchService { context, cli: args, userEnv, - forceNewWindow: args.wait || args['new-window'], + forceNewWindow: args.wait || args['new-window'] || args['new-window-if-not-first'], preferNewWindow: !args['reuse-window'], forceReuseWindow: args['reuse-window'], diffMode: args.diff diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index b9e11a0702a..5eb0da6e884 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -257,7 +257,7 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo } else if (global.macOpenFiles && global.macOpenFiles.length && (!environmentService.args._ || !environmentService.args._.length)) { windowsMainService.open({ context: OpenContext.DOCK, cli: environmentService.args, pathsToOpen: global.macOpenFiles, initialStartup: true }); // mac: open-file event received on startup } else { - windowsMainService.open({ context, cli: environmentService.args, forceNewWindow: environmentService.args['new-window'], diffMode: environmentService.args.diff, initialStartup: true }); // default: read paths from cli + windowsMainService.open({ context, cli: environmentService.args, forceNewWindow: environmentService.args['new-window'] || environmentService.args['new-window-if-not-first'], diffMode: environmentService.args.diff, initialStartup: true }); // default: read paths from cli } // Install Menu diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index a95ec8f2bea..36ecf5f1fb6 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -14,6 +14,7 @@ export interface ParsedArgs { diff?: boolean; goto?: boolean; 'new-window'?: boolean; + 'new-window-if-not-first'?: boolean; 'reuse-window'?: boolean; locale?: string; 'user-data-dir'?: string; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index caa91b82bb4..c6f6cc136c5 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -30,6 +30,7 @@ const options: minimist.Opts = { 'diff', 'goto', 'new-window', + 'new-window-if-not-first', 'reuse-window', 'performance', 'verbose', From 97ba3fddce4e9425f39b028580a512665d8c7841 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 5 Jan 2017 12:30:42 -0800 Subject: [PATCH 098/131] Pick up ts 2.1.4 (#18061) --- build/monaco/api.js | 30 +++++++++---------- build/monaco/api.ts | 30 +++++++++---------- .../src/features/codeActionProvider.ts | 4 +-- .../src/features/completionItemProvider.ts | 2 +- package.json | 8 ++--- src/vs/base/test/common/utils.ts | 19 ++++++++---- src/vs/editor/common/modes/linkComputer.ts | 2 +- .../parts/markers/common/markersModel.ts | 8 ++--- 8 files changed, 56 insertions(+), 47 deletions(-) diff --git a/build/monaco/api.js b/build/monaco/api.js index 4cab80a45a9..469b19bd79d 100644 --- a/build/monaco/api.js +++ b/build/monaco/api.js @@ -188,21 +188,21 @@ function format(text) { } function getDefaultOptions() { return { - IndentSize: 4, - TabSize: 4, - NewLineCharacter: '\r\n', - ConvertTabsToSpaces: true, - IndentStyle: ts.IndentStyle.Block, - InsertSpaceAfterCommaDelimiter: true, - InsertSpaceAfterSemicolonInForStatements: true, - InsertSpaceBeforeAndAfterBinaryOperators: true, - InsertSpaceAfterKeywordsInControlFlowStatements: true, - InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false, - InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, - InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, - InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: true, - PlaceOpenBraceOnNewLineForFunctions: false, - PlaceOpenBraceOnNewLineForControlBlocks: false, + indentSize: 4, + tabSize: 4, + newLineCharacter: '\r\n', + convertTabsToSpaces: true, + indentStyle: ts.IndentStyle.Block, + insertSpaceAfterCommaDelimiter: true, + insertSpaceAfterSemicolonInForStatements: true, + insertSpaceBeforeAndAfterBinaryOperators: true, + insertSpaceAfterKeywordsInControlFlowStatements: true, + insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: true, + placeOpenBraceOnNewLineForFunctions: false, + placeOpenBraceOnNewLineForControlBlocks: false, }; } } diff --git a/build/monaco/api.ts b/build/monaco/api.ts index 2ca2968c6c3..b399d1b1d81 100644 --- a/build/monaco/api.ts +++ b/build/monaco/api.ts @@ -217,22 +217,22 @@ function format(text:string): string { function getDefaultOptions(): ts.FormatCodeOptions { return { - IndentSize: 4, - TabSize: 4, - NewLineCharacter: '\r\n', - ConvertTabsToSpaces: true, - IndentStyle: ts.IndentStyle.Block, + indentSize: 4, + tabSize: 4, + newLineCharacter: '\r\n', + convertTabsToSpaces: true, + indentStyle: ts.IndentStyle.Block, - InsertSpaceAfterCommaDelimiter: true, - InsertSpaceAfterSemicolonInForStatements: true, - InsertSpaceBeforeAndAfterBinaryOperators: true, - InsertSpaceAfterKeywordsInControlFlowStatements: true, - InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false, - InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, - InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, - InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: true, - PlaceOpenBraceOnNewLineForFunctions: false, - PlaceOpenBraceOnNewLineForControlBlocks: false, + insertSpaceAfterCommaDelimiter: true, + insertSpaceAfterSemicolonInForStatements: true, + insertSpaceBeforeAndAfterBinaryOperators: true, + insertSpaceAfterKeywordsInControlFlowStatements: true, + insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: true, + placeOpenBraceOnNewLineForFunctions: false, + placeOpenBraceOnNewLineForControlBlocks: false, }; } } diff --git a/extensions/typescript/src/features/codeActionProvider.ts b/extensions/typescript/src/features/codeActionProvider.ts index e0ff2b69412..5c510238096 100644 --- a/extensions/typescript/src/features/codeActionProvider.ts +++ b/extensions/typescript/src/features/codeActionProvider.ts @@ -46,7 +46,7 @@ export default class TypeScriptCodeActionProvider implements CodeActionProvider public provideCodeActions(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken): Thenable { const file = this.client.asAbsolutePath(document.uri); if (!file) { - return Promise.resolve(null); + return Promise.resolve([]); } const source: Source = { @@ -99,7 +99,7 @@ export default class TypeScriptCodeActionProvider implements CodeActionProvider private onCodeAction(source: Source, workspaceEdit: WorkspaceEdit) { workspace.applyEdit(workspaceEdit).then(success => { if (!success) { - return Promise.reject(null); + return Promise.reject(false); } // TODO: Workaround for https://github.com/Microsoft/TypeScript/issues/12249 // apply formatting to the source range until TS returns formatted results diff --git a/extensions/typescript/src/features/completionItemProvider.ts b/extensions/typescript/src/features/completionItemProvider.ts index 1894dab23ba..71cb92ffd94 100644 --- a/extensions/typescript/src/features/completionItemProvider.ts +++ b/extensions/typescript/src/features/completionItemProvider.ts @@ -119,7 +119,7 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP public provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): Promise { if (this.typingsStatus.isAcquiringTypings) { - return Promise.reject({ + return Promise.reject({ label: localize('acquiringTypingsLabel', 'Acquiring typings...'), detail: localize('acquiringTypingsDetail', 'Acquiring typings definitions for IntelliSense.') }); diff --git a/package.json b/package.json index c6c21afc8cf..84846e8827e 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "eslint": "^3.4.0", "event-stream": "^3.1.7", "express": "^4.13.1", + "flatpak-bundler": "^0.1.1", "ghooks": "1.0.3", "glob": "^5.0.13", "gulp": "^3.8.9", @@ -59,6 +60,7 @@ "gulp-cssnano": "^2.1.0", "gulp-filter": "^3.0.0", "gulp-flatmap": "^1.0.0", + "gulp-image-resize": "^0.10.0", "gulp-json-editor": "^2.2.1", "gulp-mocha": "^2.1.3", "gulp-remote-src": "^0.4.0", @@ -71,8 +73,6 @@ "gulp-uglify": "^2.0.0", "gulp-util": "^3.0.6", "gulp-vinyl-zip": "^1.2.2", - "gulp-image-resize": "^0.10.0", - "flatpak-bundler": "^0.1.1", "innosetup-compiler": "^5.5.60", "is": "^3.1.0", "istanbul": "^0.3.17", @@ -91,8 +91,8 @@ "sinon": "^1.17.2", "source-map": "^0.4.4", "tslint": "^3.3.0", - "typescript": "2.0.3", - "typescript-formatter": "3.1.0", + "typescript": "^2.1.4", + "typescript-formatter": "4.0.1", "uglify-js": "2.4.8", "underscore": "^1.8.2", "vinyl": "^0.4.5", diff --git a/src/vs/base/test/common/utils.ts b/src/vs/base/test/common/utils.ts index f8693d9cd69..f69d864e509 100644 --- a/src/vs/base/test/common/utils.ts +++ b/src/vs/base/test/common/utils.ts @@ -13,18 +13,21 @@ import URI from 'vs/base/common/uri'; export class DeferredTPromise extends TPromise { - public canceled = false; + public canceled: boolean; private completeCallback: TValueCallback; private errorCallback: (err: any) => void; private progressCallback: ProgressCallback; constructor() { + let captured: any; super((c, e, p) => { - this.completeCallback = c; - this.errorCallback = e; - this.progressCallback = p; + captured = { c, e, p }; }, () => this.oncancel()); + this.canceled = false; + this.completeCallback = captured.c; + this.errorCallback = captured.e; + this.progressCallback = captured.p; } public complete(value: T) { @@ -51,7 +54,13 @@ export class DeferredPPromise extends PPromise { private progressCallback: TProgressCallback

; constructor(init: (complete: TValueCallback, error: (err: any) => void, progress: TProgressCallback

) => void = (c, e, p) => { }, oncancel?: any) { - super((c, e, p) => { this.completeCallback = c; this.errorCallback = e; this.progressCallback = p; }, oncancel ? oncancel : () => this.oncancel); + let captured: any; + super((c, e, p) => { + captured = { c, e, p }; + }, oncancel ? oncancel : () => this.oncancel); + this.completeCallback = captured.c; + this.errorCallback = captured.e; + this.progressCallback = captured.p; } private oncancel(): void { diff --git a/src/vs/editor/common/modes/linkComputer.ts b/src/vs/editor/common/modes/linkComputer.ts index ce1c82562ac..24b52884752 100644 --- a/src/vs/editor/common/modes/linkComputer.ts +++ b/src/vs/editor/common/modes/linkComputer.ts @@ -125,7 +125,7 @@ const enum CharacterClass { } const classifier = (function () { - let result = new CharacterClassifier(CharacterClass.None); + let result = new CharacterClassifier(CharacterClass.None); const FORCE_TERMINATION_CHARACTERS = ' \t<>\'\"、。。、,.:;?!@#$%&*‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…'; for (let i = 0; i < FORCE_TERMINATION_CHARACTERS.length; i++) { diff --git a/src/vs/workbench/parts/markers/common/markersModel.ts b/src/vs/workbench/parts/markers/common/markersModel.ts index bd7e75d2a24..ce4524b4ded 100644 --- a/src/vs/workbench/parts/markers/common/markersModel.ts +++ b/src/vs/workbench/parts/markers/common/markersModel.ts @@ -26,8 +26,8 @@ export class Resource { private _path: string = null; constructor(public uri: URI, public markers: Marker[], - public statistics: MarkerStatistics, - public matches: IMatch[] = []) { + public statistics: MarkerStatistics, + public matches: IMatch[] = []) { } public get path(): string { @@ -47,8 +47,8 @@ export class Resource { export class Marker { constructor(public id: string, public marker: IMarker, - public labelMatches: IMatch[] = [], - public sourceMatches: IMatch[] = []) { } + public labelMatches: IMatch[] = [], + public sourceMatches: IMatch[] = []) { } public get resource(): URI { return this.marker.resource; From 5cf85c73d5099b32aad1085aaecee2524d6cb62e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 5 Jan 2017 12:32:53 -0800 Subject: [PATCH 099/131] Use Better Range When Formatting TS Quick Fixes (#18135) **Bug** TS 2.2 includes a number of new code actions, such as implementing missing interface methods on classes. TS returns these in unedited form, so we have to format the document after applying these fixes. This formatting is not always correct using the current logic. **Fix** Try to determine the range to format using the set of text edits that TS returns to us. This is not perfect, but it should better match the actual edit that we make using the quick action --- .../src/features/codeActionProvider.ts | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/extensions/typescript/src/features/codeActionProvider.ts b/extensions/typescript/src/features/codeActionProvider.ts index 5c510238096..51a9aed5a67 100644 --- a/extensions/typescript/src/features/codeActionProvider.ts +++ b/extensions/typescript/src/features/codeActionProvider.ts @@ -5,7 +5,7 @@ 'use strict'; -import { CodeActionProvider, TextDocument, Range, CancellationToken, CodeActionContext, Command, commands, Uri, workspace, WorkspaceEdit, TextEdit } from 'vscode'; +import { CodeActionProvider, TextDocument, Range, CancellationToken, CodeActionContext, Command, commands, Uri, workspace, WorkspaceEdit, TextEdit, Position } from 'vscode'; import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; @@ -101,9 +101,27 @@ export default class TypeScriptCodeActionProvider implements CodeActionProvider if (!success) { return Promise.reject(false); } + + let firstEdit: TextEdit | null = null; + for (const [uri, edits] of workspaceEdit.entries()) { + if (uri.fsPath === source.uri.fsPath) { + firstEdit = edits[0]; + break; + } + } + + if (!firstEdit) { + return true; + } + + const newLines = firstEdit.newText.match(/\n/g); + const editedRange = new Range( + new Position(firstEdit.range.start.line, 0), + new Position(firstEdit.range.end.line + 1 + (newLines ? newLines.length : 0), 0)); + // TODO: Workaround for https://github.com/Microsoft/TypeScript/issues/12249 // apply formatting to the source range until TS returns formatted results - return commands.executeCommand('vscode.executeFormatRangeProvider', source.uri, source.range, {}).then((edits: TextEdit[]) => { + return commands.executeCommand('vscode.executeFormatRangeProvider', source.uri, editedRange, {}).then((edits: TextEdit[]) => { if (!edits || !edits.length) { return false; } From 9eb91d2de47bdec0ee0e80b7a3cc224d5bad44fc Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 5 Jan 2017 12:33:29 -0800 Subject: [PATCH 100/131] Show Error Message if TypeScript Settings Update Fails (#18130) Fixes #18127 Show error messages if the workspace or user settings update fails --- .../typescript/src/typescriptServiceClient.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/extensions/typescript/src/typescriptServiceClient.ts b/extensions/typescript/src/typescriptServiceClient.ts index ad18e62f4c3..12e36d958e9 100644 --- a/extensions/typescript/src/typescriptServiceClient.ts +++ b/extensions/typescript/src/typescriptServiceClient.ts @@ -346,17 +346,20 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient switch (selected.id) { case MessageAction.useLocal: let pathValue = './node_modules/typescript/lib'; - tsConfig.update('tsdk', pathValue, false); - window.showInformationMessage(localize('updatedtsdk', 'Updated workspace setting \'typescript.tsdk\' to {0}', pathValue)); + tsConfig.update('tsdk', pathValue, false).then( + () => window.showInformationMessage(localize('updatedtsdk', 'Updated workspace setting \'typescript.tsdk\' to {0}', pathValue)), + () => window.showErrorMessage(localize('updateTsdkFailed', 'Could not update the \'typescript.tsdk\' workspace setting. Please check that the workspace settings file is valid'))); showVersionStatusItem = true; return localModulePath; case MessageAction.useBundled: - tsConfig.update(checkWorkspaceVersionKey, false, false); - window.showInformationMessage(localize('updateLocalWorkspaceCheck', 'Updated workspace setting \'typescript.check.workspaceVersion\' to false')); + tsConfig.update(checkWorkspaceVersionKey, false, false).then( + () => window.showInformationMessage(localize('updateLocalWorkspaceCheck', 'Updated workspace setting \'typescript.check.workspaceVersion\' to false')), + () => window.showErrorMessage(localize('updateLocalWorkspaceCheckFailed', 'Could not update the \'typescript.check.workspaceVersion\' workspace setting. Please check that the workspace settings file is valid'))); return modulePath; case MessageAction.neverCheckLocalVersion: - window.showInformationMessage(localize('updateGlobalWorkspaceCheck', 'Updated user setting \'typescript.check.workspaceVersion\' to false')); - tsConfig.update(checkWorkspaceVersionKey, false, true); + tsConfig.update(checkWorkspaceVersionKey, false, true).then( + () => window.showInformationMessage(localize('updateGlobalWorkspaceCheck', 'Updated user setting \'typescript.check.workspaceVersion\' to false')), + () => window.showErrorMessage(localize('updateGlobalWorkspaceCheckFailed', 'Could not update \'typescript.check.workspaceVersion\' user setting. Please check that your user settings file is valid'))); return modulePath; default: return modulePath; From 5ab752310385fd238c3e3b4d43658ddd3579360d Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 5 Jan 2017 22:10:50 +0100 Subject: [PATCH 101/131] debug: dispose created internal source models on debug session end #18148 --- .../parts/debug/browser/debugContentProvider.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/debugContentProvider.ts b/src/vs/workbench/parts/debug/browser/debugContentProvider.ts index e74b027c188..ceb3ae21200 100644 --- a/src/vs/workbench/parts/debug/browser/debugContentProvider.ts +++ b/src/vs/workbench/parts/debug/browser/debugContentProvider.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as lifecycle from 'vs/base/common/lifecycle'; import uri from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { guessMimeTypes } from 'vs/base/common/mime'; @@ -10,13 +11,15 @@ import { IModel } from 'vs/editor/common/editorCommon'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { ITextModelResolverService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; -import { DEBUG_SCHEME, IDebugService } from 'vs/workbench/parts/debug/common/debug'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { DEBUG_SCHEME, IDebugService, State } from 'vs/workbench/parts/debug/common/debug'; import { Model } from 'vs/workbench/parts/debug/common/debugModel'; import { Source } from 'vs/workbench/parts/debug/common/debugSource'; -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; export class DebugContentProvider implements IWorkbenchContribution, ITextModelContentProvider { + private modelsToDispose: IModel[]; + constructor( @ITextModelResolverService textModelResolverService: ITextModelResolverService, @IDebugService private debugService: IDebugService, @@ -24,6 +27,12 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC @IModeService private modeService: IModeService ) { textModelResolverService.registerTextModelContentProvider(DEBUG_SCHEME, this); + this.modelsToDispose = []; + this.debugService.onDidChangeState(() => { + if (this.debugService.state === State.Inactive) { + this.modelsToDispose = lifecycle.dispose(this.modelsToDispose); + } + }); } public getId(): string { @@ -39,8 +48,10 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC return process.session.source({ sourceReference: Source.getSourceReference(resource) }).then(response => { const mime = response.body.mimeType || guessMimeTypes(resource.toString())[0]; const modePromise = this.modeService.getOrCreateMode(mime); + const model = this.modelService.createModel(response.body.content, modePromise, resource); + this.modelsToDispose.push(model); - return this.modelService.createModel(response.body.content, modePromise, resource); + return model; }, err => { (this.debugService.getModel()).sourceIsUnavailable(resource); return err; From 97f3b53f09566e22bb6ee107acf6c06804e317f5 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 5 Jan 2017 22:58:37 +0100 Subject: [PATCH 102/131] debug: use proper Map for threads fixes #18154 --- .../parts/debug/common/debugModel.ts | 64 ++++++++++--------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index 68921520b50..313bceae8bc 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -477,10 +477,10 @@ export class Thread implements debug.IThread { export class Process implements debug.IProcess { - private threads: { [reference: number]: Thread; }; + private threads: Map; constructor(public name: string, private _session: debug.ISession & debug.ITreeElement) { - this.threads = {}; + this.threads = new Map(); } public get session(): debug.ISession { @@ -488,11 +488,13 @@ export class Process implements debug.IProcess { } public getThread(threadId: number): Thread { - return this.threads[threadId]; + return this.threads.get(threadId); } public getAllThreads(): debug.IThread[] { - return Object.keys(this.threads).map(key => this.threads[key]); + const result = []; + this.threads.forEach(t => result.push(t)); + return result; } public getId(): string { @@ -501,66 +503,66 @@ export class Process implements debug.IProcess { public rawUpdate(data: debug.IRawModelUpdate): void { - if (data.thread && !this.threads[data.threadId]) { + if (data.thread && !this.threads.has(data.threadId)) { // A new thread came in, initialize it. - this.threads[data.threadId] = new Thread(this, data.thread.name, data.thread.id); + this.threads.set(data.threadId, new Thread(this, data.thread.name, data.thread.id)); } if (data.stoppedDetails) { // Set the availability of the threads' callstacks depending on // whether the thread is stopped or not if (data.allThreadsStopped) { - Object.keys(this.threads).forEach(ref => { + this.threads.forEach(thread => { // Only update the details if all the threads are stopped // because we don't want to overwrite the details of other // threads that have stopped for a different reason - this.threads[ref].stoppedDetails = clone(data.stoppedDetails); - this.threads[ref].stopped = true; - this.threads[ref].clearCallStack(); + thread.stoppedDetails = clone(data.stoppedDetails); + thread.stopped = true; + thread.clearCallStack(); }); } else { // One thread is stopped, only update that thread. - this.threads[data.threadId].stoppedDetails = data.stoppedDetails; - this.threads[data.threadId].clearCallStack(); - this.threads[data.threadId].stopped = true; + const thread = this.threads.get(data.threadId); + thread.stoppedDetails = data.stoppedDetails; + thread.clearCallStack(); + thread.stopped = true; } } } public clearThreads(removeThreads: boolean, reference: number = undefined): void { if (reference) { - if (this.threads[reference]) { - this.threads[reference].clearCallStack(); - this.threads[reference].stoppedDetails = undefined; - this.threads[reference].stopped = false; + if (this.threads.has(reference)) { + const thread = this.threads.get(reference); + thread.clearCallStack(); + thread.stoppedDetails = undefined; + thread.stopped = false; if (removeThreads) { - delete this.threads[reference]; + this.threads.delete(reference); } } } else { - Object.keys(this.threads).forEach(ref => { - this.threads[ref].clearCallStack(); - this.threads[ref].stoppedDetails = undefined; - this.threads[ref].stopped = false; + this.threads.forEach(thread => { + thread.clearCallStack(); + thread.stoppedDetails = undefined; + thread.stopped = false; }); if (removeThreads) { - this.threads = {}; + this.threads.clear(); ExpressionContainer.allValues = {}; } } } public sourceIsUnavailable(uri: uri): void { - Object.keys(this.threads).forEach(key => { - if (this.threads[key].getCachedCallStack()) { - this.threads[key].getCachedCallStack().forEach(stackFrame => { - if (stackFrame.source.uri.toString() === uri.toString()) { - stackFrame.source.available = false; - } - }); - } + this.threads.forEach(thread => { + thread.getCallStack().forEach(stackFrame => { + if (stackFrame.source.uri.toString() === uri.toString()) { + stackFrame.source.available = false; + } + }); }); } From ebaed955b920b583857d2633f036ab5e58e6c89f Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 5 Jan 2017 23:24:15 +0100 Subject: [PATCH 103/131] debugEditorModelManager: use es Map --- .../debug/browser/debugEditorModelManager.ts | 97 ++++++++++--------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts b/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts index dac4d20c36e..258c0a29a13 100644 --- a/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts +++ b/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts @@ -18,7 +18,7 @@ interface IDebugEditorModelData { toDispose: lifecycle.IDisposable[]; breakpointDecorationIds: string[]; breakpointLines: number[]; - breakpointDecorationsAsMap: { [decorationId: string]: boolean; }; + breakpointDecorationsAsMap: Map; currentStackDecorations: string[]; topStackFrameRange: IRange; dirty: boolean; @@ -29,16 +29,14 @@ const stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges; export class DebugEditorModelManager implements IWorkbenchContribution { static ID = 'breakpointManager'; - private modelData: { - [modelUrl: string]: IDebugEditorModelData; - }; + private modelDataMap: Map; private toDispose: lifecycle.IDisposable[]; constructor( @IModelService private modelService: IModelService, @IDebugService private debugService: IDebugService ) { - this.modelData = Object.create(null); + this.modelDataMap = new Map(); this.toDispose = []; this.registerListeners(); } @@ -48,14 +46,14 @@ export class DebugEditorModelManager implements IWorkbenchContribution { } public dispose(): void { - Object.keys(this.modelData).forEach(modelUriStr => { - lifecycle.dispose(this.modelData[modelUriStr].toDispose); - this.modelData[modelUriStr].model.deltaDecorations(this.modelData[modelUriStr].breakpointDecorationIds, []); - this.modelData[modelUriStr].model.deltaDecorations(this.modelData[modelUriStr].currentStackDecorations, []); + this.modelDataMap.forEach(modelData => { + lifecycle.dispose(modelData.toDispose); + modelData.model.deltaDecorations(modelData.breakpointDecorationIds, []); + modelData.model.deltaDecorations(modelData.currentStackDecorations, []); }); this.toDispose = lifecycle.dispose(this.toDispose); - this.modelData = null; + this.modelDataMap.clear(); } private registerListeners(): void { @@ -67,7 +65,7 @@ export class DebugEditorModelManager implements IWorkbenchContribution { this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(() => this.onFocusStackFrame())); this.toDispose.push(this.debugService.onDidChangeState(() => { if (this.debugService.state === State.Inactive) { - Object.keys(this.modelData).forEach(key => this.modelData[key].dirty = false); + this.modelDataMap.forEach(modelData => modelData.dirty = false); } })); } @@ -80,40 +78,41 @@ export class DebugEditorModelManager implements IWorkbenchContribution { const breakPointDecorations = model.deltaDecorations([], this.createBreakpointDecorations(breakpoints)); const toDispose: lifecycle.IDisposable[] = [model.onDidChangeDecorations((e) => this.onModelDecorationsChanged(modelUrlStr, e))]; + const breakpointDecorationsAsMap = new Map(); + breakPointDecorations.forEach(bpd => breakpointDecorationsAsMap.set(bpd, true)); - this.modelData[modelUrlStr] = { + this.modelDataMap.set(modelUrlStr, { model: model, toDispose: toDispose, breakpointDecorationIds: breakPointDecorations, breakpointLines: breakpoints.map(bp => bp.lineNumber), - breakpointDecorationsAsMap: objects.toObject(breakPointDecorations, key => key, key => true), + breakpointDecorationsAsMap, currentStackDecorations: currentStackDecorations, topStackFrameRange: null, dirty: false - }; + }); } private onModelRemoved(model: IModel): void { const modelUriStr = model.uri.toString(); - if (this.modelData[modelUriStr]) { - lifecycle.dispose(this.modelData[modelUriStr].toDispose); - delete this.modelData[modelUriStr]; + if (this.modelDataMap.has(modelUriStr)) { + lifecycle.dispose(this.modelDataMap.get(modelUriStr).toDispose); + this.modelDataMap.delete(modelUriStr); } } // call stack management. Represent data coming from the debug service. private onFocusStackFrame(): void { - Object.keys(this.modelData).forEach(modelUrlStr => { - const modelData = this.modelData[modelUrlStr]; - modelData.currentStackDecorations = modelData.model.deltaDecorations(modelData.currentStackDecorations, this.createCallStackDecorations(modelUrlStr)); + this.modelDataMap.forEach((modelData, uri) => { + modelData.currentStackDecorations = modelData.model.deltaDecorations(modelData.currentStackDecorations, this.createCallStackDecorations(uri)); }); } - private createCallStackDecorations(modelUrlStr: string): IModelDeltaDecoration[] { + private createCallStackDecorations(modelUriStr: string): IModelDeltaDecoration[] { const result: IModelDeltaDecoration[] = []; const stackFrame = this.debugService.getViewModel().focusedStackFrame; - if (!stackFrame || stackFrame.source.uri.toString() !== modelUrlStr) { + if (!stackFrame || stackFrame.source.uri.toString() !== modelUriStr) { return result; } @@ -140,15 +139,16 @@ export class DebugEditorModelManager implements IWorkbenchContribution { range: wholeLineRange }); - if (this.modelData[modelUrlStr]) { - if (this.modelData[modelUrlStr].topStackFrameRange && this.modelData[modelUrlStr].topStackFrameRange.startLineNumber === wholeLineRange.startLineNumber && - this.modelData[modelUrlStr].topStackFrameRange.startColumn !== wholeLineRange.startColumn) { + if (this.modelDataMap.has(modelUriStr)) { + const modelData = this.modelDataMap.get(modelUriStr); + if (modelData.topStackFrameRange && modelData.topStackFrameRange.startLineNumber === wholeLineRange.startLineNumber && + modelData.topStackFrameRange.startColumn !== wholeLineRange.startColumn) { result.push({ options: DebugEditorModelManager.TOP_STACK_FRAME_COLUMN_DECORATION, range: wholeLineRange }); } - this.modelData[modelUrlStr].topStackFrameRange = wholeLineRange; + modelData.topStackFrameRange = wholeLineRange; } } } else { @@ -168,22 +168,21 @@ export class DebugEditorModelManager implements IWorkbenchContribution { // breakpoints management. Represent data coming from the debug service and also send data back. private onModelDecorationsChanged(modelUrlStr: string, e: IModelDecorationsChangedEvent): void { - const modelData = this.modelData[modelUrlStr]; - let myDecorationsCount = Object.keys(modelData.breakpointDecorationsAsMap).length; - if (myDecorationsCount === 0) { + const modelData = this.modelDataMap.get(modelUrlStr); + if (modelData.breakpointDecorationsAsMap.size === 0) { // I have no decorations return; } - if (!e.changedDecorations.some(decorationId => modelData.breakpointDecorationsAsMap[decorationId])) { + if (!e.changedDecorations.some(decorationId => modelData.breakpointDecorationsAsMap.has(decorationId))) { // nothing to do, my decorations did not change. return; } const data: IRawBreakpoint[] = []; - const lineToBreakpointDataMap: { [key: number]: IBreakpoint } = {}; + const lineToBreakpointDataMap = new Map(); this.debugService.getModel().getBreakpoints().filter(bp => bp.uri.toString() === modelUrlStr).forEach(bp => { - lineToBreakpointDataMap[bp.lineNumber] = bp; + lineToBreakpointDataMap.set(bp.lineNumber, bp); }); const modelUri = modelData.model.uri; @@ -192,13 +191,14 @@ export class DebugEditorModelManager implements IWorkbenchContribution { const lineNumber = modelData.breakpointLines[i]; // check if the line got deleted. if (decorationRange.endColumn - decorationRange.startColumn > 0) { + const breakpoint = lineToBreakpointDataMap.get(lineNumber); // since we know it is collapsed, it cannot grow to multiple lines data.push({ lineNumber: decorationRange.startLineNumber, - enabled: lineToBreakpointDataMap[lineNumber].enabled, - condition: lineToBreakpointDataMap[lineNumber].condition, - hitCondition: lineToBreakpointDataMap[lineNumber].hitCondition, - column: lineToBreakpointDataMap[lineNumber].column + enabled: breakpoint.enabled, + condition: breakpoint.condition, + hitCondition: breakpoint.hitCondition, + column: breakpoint.column }); } } @@ -213,31 +213,32 @@ export class DebugEditorModelManager implements IWorkbenchContribution { } private onBreakpointsChange(): void { - const breakpointsMap: { [key: string]: IBreakpoint[] } = Object.create(null); + const breakpointsMap = new Map(); this.debugService.getModel().getBreakpoints().forEach(bp => { const uriStr = bp.uri.toString(); - if (breakpointsMap[uriStr]) { - breakpointsMap[uriStr].push(bp); + if (breakpointsMap.has(uriStr)) { + breakpointsMap.get(uriStr).push(bp); } else { - breakpointsMap[uriStr] = [bp]; + breakpointsMap.set(uriStr, [bp]); } }); - Object.keys(breakpointsMap).forEach(modelUriStr => { - if (this.modelData[modelUriStr]) { - this.updateBreakpoints(this.modelData[modelUriStr], breakpointsMap[modelUriStr]); + breakpointsMap.forEach((bps, uri) => { + if (this.modelDataMap.has(uri)) { + this.updateBreakpoints(this.modelDataMap.get(uri), breakpointsMap.get(uri)); } }); - Object.keys(this.modelData).forEach(modelUriStr => { - if (!breakpointsMap[modelUriStr]) { - this.updateBreakpoints(this.modelData[modelUriStr], []); + this.modelDataMap.forEach((modelData, uri) => { + if (!breakpointsMap.has(uri)) { + this.updateBreakpoints(modelData, []); } }); } private updateBreakpoints(modelData: IDebugEditorModelData, newBreakpoints: IBreakpoint[]): void { modelData.breakpointDecorationIds = modelData.model.deltaDecorations(modelData.breakpointDecorationIds, this.createBreakpointDecorations(newBreakpoints)); - modelData.breakpointDecorationsAsMap = objects.toObject(modelData.breakpointDecorationIds, key => key, (key) => true); + modelData.breakpointDecorationsAsMap.clear(); + modelData.breakpointDecorationIds.forEach(id => modelData.breakpointDecorationsAsMap.set(id, true)); modelData.breakpointLines = newBreakpoints.map(bp => bp.lineNumber); } @@ -254,7 +255,7 @@ export class DebugEditorModelManager implements IWorkbenchContribution { const activated = this.debugService.getModel().areBreakpointsActivated(); const state = this.debugService.state; const debugActive = state === State.Running || state === State.Stopped || state === State.Initializing; - const modelData = this.modelData[breakpoint.uri.toString()]; + const modelData = this.modelDataMap.get(breakpoint.uri.toString()); let result = (!breakpoint.enabled || !activated) ? DebugEditorModelManager.BREAKPOINT_DISABLED_DECORATION : debugActive && modelData && modelData.dirty && !breakpoint.verified ? DebugEditorModelManager.BREAKPOINT_DIRTY_DECORATION : From 140f1ec0a3a671e84681d4dc2b10f01cdb485423 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 5 Jan 2017 23:51:06 +0100 Subject: [PATCH 104/131] debug: use Set and Map --- .../parts/debug/common/debugModel.ts | 10 ++-- .../parts/debug/common/replHistory.ts | 16 +++--- .../debugConfigurationManager.ts | 10 ++-- .../debug/electron-browser/debugService.ts | 52 +++++++++---------- .../workbench/parts/debug/node/v8Protocol.ts | 10 ++-- 5 files changed, 47 insertions(+), 51 deletions(-) diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index 313bceae8bc..9a6b23de05b 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -91,7 +91,7 @@ export class OutputNameValueElement extends AbstractOutputElement implements deb export class ExpressionContainer implements debug.IExpressionContainer { - public static allValues: { [id: string]: string } = {}; + public static allValues: Map = new Map(); // Use chunks to support variable paging #9537 private static BASE_CHUNK_SIZE = 100; @@ -174,9 +174,9 @@ export class ExpressionContainer implements debug.IExpressionContainer { public set value(value: string) { this._value = value; - this.valueChanged = ExpressionContainer.allValues[this.getId()] && - ExpressionContainer.allValues[this.getId()] !== Expression.DEFAULT_VALUE && ExpressionContainer.allValues[this.getId()] !== value; - ExpressionContainer.allValues[this.getId()] = value; + this.valueChanged = ExpressionContainer.allValues.get(this.getId()) && + ExpressionContainer.allValues.get(this.getId()) !== Expression.DEFAULT_VALUE && ExpressionContainer.allValues.get(this.getId()) !== value; + ExpressionContainer.allValues.set(this.getId(), value); } } @@ -551,7 +551,7 @@ export class Process implements debug.IProcess { if (removeThreads) { this.threads.clear(); - ExpressionContainer.allValues = {}; + ExpressionContainer.allValues.clear(); } } } diff --git a/src/vs/workbench/parts/debug/common/replHistory.ts b/src/vs/workbench/parts/debug/common/replHistory.ts index 9ed159fe91b..92beb99600d 100644 --- a/src/vs/workbench/parts/debug/common/replHistory.ts +++ b/src/vs/workbench/parts/debug/common/replHistory.ts @@ -17,12 +17,12 @@ export class ReplHistory { private historyPointer: number; private currentExpressionStoredMarkers: boolean; - private historyOverwrites: { [position: string]: string; }; + private historyOverwrites: Map; constructor(private history: string[]) { this.historyPointer = this.history.length; this.currentExpressionStoredMarkers = false; - this.historyOverwrites = {}; + this.historyOverwrites = new Map(); } public next(): string { @@ -48,8 +48,8 @@ export class ReplHistory { this.historyPointer = newPointer; // check for overwrite - if (this.historyOverwrites && this.historyOverwrites[newPointer.toString()]) { - return this.historyOverwrites[newPointer.toString()]; + if (this.historyOverwrites.has(newPointer.toString())) { + return this.historyOverwrites.get(newPointer.toString()); } return this.history[newPointer]; @@ -78,11 +78,7 @@ export class ReplHistory { // keep edits that are made to history items up until the user actually evaluates a expression else { - if (!this.historyOverwrites) { - this.historyOverwrites = {}; - } - - this.historyOverwrites[previousPointer.toString()] = expression; + this.historyOverwrites.set(previousPointer.toString(), expression); } } @@ -104,7 +100,7 @@ export class ReplHistory { this.currentExpressionStoredMarkers = false; // reset overwrites - this.historyOverwrites = null; + this.historyOverwrites.clear(); } public save(): string[] { diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index 3519e425649..ecec45008b0 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -193,7 +193,7 @@ jsonRegistry.registerSchema(schemaId, schema); export class ConfigurationManager implements debug.IConfigurationManager { private adapters: Adapter[]; - private allModeIdsForBreakpoints: { [key: string]: boolean }; + private breakpointModeIdsSet: Set; constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService, @@ -208,7 +208,7 @@ export class ConfigurationManager implements debug.IConfigurationManager { ) { this.adapters = []; this.registerListeners(); - this.allModeIdsForBreakpoints = {}; + this.breakpointModeIdsSet = new Set(); } private registerListeners(): void { @@ -220,7 +220,7 @@ export class ConfigurationManager implements debug.IConfigurationManager { } if (rawAdapter.enableBreakpointsFor) { rawAdapter.enableBreakpointsFor.languageIds.forEach(modeId => { - this.allModeIdsForBreakpoints[modeId] = true; + this.breakpointModeIdsSet.add(modeId); }); } @@ -250,7 +250,7 @@ export class ConfigurationManager implements debug.IConfigurationManager { breakpointsExtPoint.setHandler(extensions => { extensions.forEach(ext => { ext.value.forEach(breakpoints => { - this.allModeIdsForBreakpoints[breakpoints.language] = true; + this.breakpointModeIdsSet.add(breakpoints.language); }); }); }); @@ -386,6 +386,6 @@ export class ConfigurationManager implements debug.IConfigurationManager { const mode = model ? model.getMode() : null; const modeId = mode ? mode.getId() : null; - return !!this.allModeIdsForBreakpoints[modeId]; + return this.breakpointModeIdsSet.has(modeId); } } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 910bbd3b8bb..75c04ff7cde 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -59,7 +59,7 @@ const DEBUG_SELECTED_CONFIG_NAME_KEY = 'debug.selectedconfigname'; export class DebugService implements debug.IDebugService { public _serviceBrand: any; - private sessionStates: { [id: string]: debug.State }; + private sessionStates: Map; private _onDidChangeState: Emitter; private model: Model; private viewModel: ViewModel; @@ -67,9 +67,9 @@ export class DebugService implements debug.IDebugService { private customTelemetryService: ITelemetryService; private lastTaskEvent: TaskEvent; private toDispose: lifecycle.IDisposable[]; - private toDisposeOnSessionEnd: { [id: string]: lifecycle.IDisposable[] }; + private toDisposeOnSessionEnd: Map; private inDebugMode: IContextKey; - private breakpointsToSendOnResourceSaved: { [uri: string]: boolean }; + private breakpointsToSendOnResourceSaved: Set; constructor( @IStorageService private storageService: IStorageService, @@ -93,10 +93,10 @@ export class DebugService implements debug.IDebugService { @IConfigurationService private configurationService: IConfigurationService ) { this.toDispose = []; - this.toDisposeOnSessionEnd = {}; - this.breakpointsToSendOnResourceSaved = {}; + this.toDisposeOnSessionEnd = new Map(); + this.breakpointsToSendOnResourceSaved = new Set(); this._onDidChangeState = new Emitter(); - this.sessionStates = {}; + this.sessionStates = new Map(); this.configurationManager = this.instantiationService.createInstance(ConfigurationManager); this.inDebugMode = debug.CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); @@ -236,8 +236,8 @@ export class DebugService implements debug.IDebugService { } private registerSessionListeners(process: Process, session: RawDebugSession): void { - this.toDisposeOnSessionEnd[session.getId()].push(session); - this.toDisposeOnSessionEnd[session.getId()].push(session.onDidInitialize(event => { + this.toDisposeOnSessionEnd.get(session.getId()).push(session); + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidInitialize(event => { aria.status(nls.localize('debuggingStarted', "Debugging started.")); const sendConfigurationDone = () => { if (session && session.configuration.capabilities.supportsConfigurationDoneRequest) { @@ -255,7 +255,7 @@ export class DebugService implements debug.IDebugService { .done(() => this.fetchThreads(session), errors.onUnexpectedError); })); - this.toDisposeOnSessionEnd[session.getId()].push(session.onDidStop(event => { + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidStop(event => { this.setStateAndEmit(session.getId(), debug.State.Stopped); const threadId = event.body.threadId; @@ -290,7 +290,7 @@ export class DebugService implements debug.IDebugService { }, errors.onUnexpectedError); })); - this.toDisposeOnSessionEnd[session.getId()].push(session.onDidThread(event => { + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidThread(event => { if (event.body.reason === 'started') { this.fetchThreads(session).done(undefined, errors.onUnexpectedError); } else if (event.body.reason === 'exited') { @@ -298,7 +298,7 @@ export class DebugService implements debug.IDebugService { } })); - this.toDisposeOnSessionEnd[session.getId()].push(session.onDidTerminateDebugee(event => { + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidTerminateDebugee(event => { aria.status(nls.localize('debuggingStopped', "Debugging stopped.")); if (session && session.getId() === event.body.sessionId) { if (event.body && typeof event.body.restart === 'boolean' && event.body.restart) { @@ -309,7 +309,7 @@ export class DebugService implements debug.IDebugService { } })); - this.toDisposeOnSessionEnd[session.getId()].push(session.onDidContinued(event => { + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidContinued(event => { const threadId = event.body.allThreadsContinued ? undefined : event.body.threadId; this.model.clearThreads(session.getId(), false, threadId); if (this.viewModel.focusedProcess.getId() === session.getId()) { @@ -318,7 +318,7 @@ export class DebugService implements debug.IDebugService { this.setStateAndEmit(session.getId(), session.requestType === debug.SessionRequestType.LAUNCH_NO_DEBUG ? debug.State.RunningNoDebug : debug.State.Running); })); - this.toDisposeOnSessionEnd[session.getId()].push(session.onDidOutput(event => { + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidOutput(event => { if (!event.body) { return; } @@ -344,7 +344,7 @@ export class DebugService implements debug.IDebugService { } })); - this.toDisposeOnSessionEnd[session.getId()].push(session.onDidBreakpoint(event => { + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidBreakpoint(event => { const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined; const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop(); if (breakpoint) { @@ -357,9 +357,9 @@ export class DebugService implements debug.IDebugService { } })); - this.toDisposeOnSessionEnd[session.getId()].push(session.onDidExitAdapter(event => { + this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidExitAdapter(event => { // 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905 - if (session && session.configuration.type === 'extensionHost' && this.sessionStates[session.getId()] === debug.State.RunningNoDebug) { + if (session && session.configuration.type === 'extensionHost' && this.sessionStates.get(session.getId()) === debug.State.RunningNoDebug) { this.windowsService.closeExtensionHostWindow(this.contextService.getWorkspace().resource.fsPath); } if (session && session.getId() === event.body.sessionId) { @@ -432,11 +432,11 @@ export class DebugService implements debug.IDebugService { const focusedProcess = this.viewModel.focusedProcess; if (focusedProcess) { - return this.sessionStates[focusedProcess.getId()]; + return this.sessionStates.get(focusedProcess.getId()); } const processes = this.model.getProcesses(); if (processes.length > 0) { - return this.sessionStates[processes[0].getId()]; + return this.sessionStates.get(processes[0].getId()); } return debug.State.Inactive; @@ -447,7 +447,7 @@ export class DebugService implements debug.IDebugService { } private setStateAndEmit(sessionId: string, newState: debug.State): void { - this.sessionStates[sessionId] = newState; + this.sessionStates.set(sessionId, newState); this._onDidChangeState.fire(); } @@ -664,9 +664,9 @@ export class DebugService implements debug.IDebugService { if (!this.viewModel.focusedProcess) { this.focusStackFrameAndEvaluate(null, process); } - this.toDisposeOnSessionEnd[session.getId()] = []; + this.toDisposeOnSessionEnd.set(session.getId(), []); if (client) { - this.toDisposeOnSessionEnd[session.getId()].push(client); + this.toDisposeOnSessionEnd.get(session.getId()).push(client); } this.registerSessionListeners(process, session); @@ -839,7 +839,7 @@ export class DebugService implements debug.IDebugService { }); try { - this.toDisposeOnSessionEnd[session.getId()] = lifecycle.dispose(this.toDisposeOnSessionEnd[session.getId()]); + this.toDisposeOnSessionEnd.set(session.getId(), lifecycle.dispose(this.toDisposeOnSessionEnd.get(session.getId()))); } catch (e) { // an internal module might be open so the dispose can throw -> ignore and continue with stop session. } @@ -896,7 +896,7 @@ export class DebugService implements debug.IDebugService { } if (this.textFileService.isDirty(modelUri)) { // Only send breakpoints for a file once it is not dirty #8077 - this.breakpointsToSendOnResourceSaved[modelUri.toString()] = true; + this.breakpointsToSendOnResourceSaved.add(modelUri.toString()); return TPromise.as(null); } @@ -991,8 +991,8 @@ export class DebugService implements debug.IDebugService { fileChangesEvent.contains(bp.uri, FileChangeType.DELETED))); fileChangesEvent.getUpdated().forEach(event => { - if (this.breakpointsToSendOnResourceSaved[event.resource.toString()]) { - this.breakpointsToSendOnResourceSaved[event.resource.toString()] = false; + if (this.breakpointsToSendOnResourceSaved.has(event.resource.toString())) { + this.breakpointsToSendOnResourceSaved.delete(event.resource.toString()); this.sendBreakpoints(event.resource, true).done(null, errors.onUnexpectedError); } }); @@ -1008,7 +1008,7 @@ export class DebugService implements debug.IDebugService { } public dispose(): void { - Object.keys(this.toDisposeOnSessionEnd).forEach(key => lifecycle.dispose(this.toDisposeOnSessionEnd[key])); + this.toDisposeOnSessionEnd.forEach(toDispose => lifecycle.dispose(toDispose)); this.toDispose = lifecycle.dispose(this.toDispose); } } diff --git a/src/vs/workbench/parts/debug/node/v8Protocol.ts b/src/vs/workbench/parts/debug/node/v8Protocol.ts index 3dd75e0e8db..8c368528db6 100644 --- a/src/vs/workbench/parts/debug/node/v8Protocol.ts +++ b/src/vs/workbench/parts/debug/node/v8Protocol.ts @@ -13,14 +13,14 @@ export abstract class V8Protocol { private outputStream: stream.Writable; private sequence: number; - private pendingRequests: { [id: number]: (e: DebugProtocol.Response) => void; }; + private pendingRequests: Map void>; private rawData: Buffer; private contentLength: number; constructor(private id: string) { this.sequence = 1; this.contentLength = -1; - this.pendingRequests = {}; + this.pendingRequests = new Map void>(); this.rawData = new Buffer(0); } @@ -77,7 +77,7 @@ export abstract class V8Protocol { if (clb) { // store callback for this request - this.pendingRequests[request.seq] = clb; + this.pendingRequests.set(request.seq, clb); } } @@ -130,9 +130,9 @@ export abstract class V8Protocol { break; case 'response': const response = rawData; - const clb = this.pendingRequests[response.request_seq]; + const clb = this.pendingRequests.get(response.request_seq); if (clb) { - delete this.pendingRequests[response.request_seq]; + this.pendingRequests.delete(response.request_seq); clb(response); } break; From 83db3cad0c9a05dac75a7472b3b81b39ace243f7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 5 Jan 2017 17:03:39 -0800 Subject: [PATCH 105/131] Fix Autolink Syntax Highlighting in Markdown (#18201) Fixes #18197 **Bug** Autolinks that start a line in markdown are currently parsed as as html content **Fix** Restrict the html element parser a little more so that we don't match tags html tags that look like `` --- extensions/markdown/syntaxes/markdown.tmLanguage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/markdown/syntaxes/markdown.tmLanguage b/extensions/markdown/syntaxes/markdown.tmLanguage index 5839acf6301..238afa870ec 100644 --- a/extensions/markdown/syntaxes/markdown.tmLanguage +++ b/extensions/markdown/syntaxes/markdown.tmLanguage @@ -355,7 +355,7 @@ begin - (^|\G)\s*(?=(<[a-zA-Z0-9\-].*>|</[a-zA-Z0-9\-]>)\s*$) + (^|\G)\s*(?=(<[a-zA-Z0-9\-](/?>|\s.*?>)|</[a-zA-Z0-9\-]>)\s*$) patterns From 7efb29292aac851ef1f3565e815c9412e9332b4a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 6 Jan 2017 10:16:21 +0100 Subject: [PATCH 106/131] use off dom canvas for faster text measurements, #18211 --- src/vs/editor/browser/config/configuration.ts | 93 ++----------------- 1 file changed, 7 insertions(+), 86 deletions(-) diff --git a/src/vs/editor/browser/config/configuration.ts b/src/vs/editor/browser/config/configuration.ts index 26cdc0c5193..34a5c02a2fe 100644 --- a/src/vs/editor/browser/config/configuration.ts +++ b/src/vs/editor/browser/config/configuration.ts @@ -41,41 +41,6 @@ class CSSBasedConfigurationCache { } } -class CharWidthReader { - - private _chr: string; - private _width: number; - - public get width(): number { return this._width; } - - constructor(chr: string) { - this._chr = chr; - this._width = 0; - } - - public render(out: HTMLSpanElement): void { - if (this._chr === ' ') { - let htmlString = ' '; - // Repeat character 256 (2^8) times - for (let i = 0; i < 8; i++) { - htmlString += htmlString; - } - out.innerHTML = htmlString; - } else { - let testString = this._chr; - // Repeat character 256 (2^8) times - for (let i = 0; i < 8; i++) { - testString += testString; - } - out.textContent = testString; - } - } - - public read(out: HTMLSpanElement): void { - this._width = out.offsetWidth / 256; - } -} - class CSSBasedConfiguration extends Disposable { public static INSTANCE = new CSSBasedConfiguration(); @@ -154,59 +119,15 @@ class CSSBasedConfiguration extends Disposable { } } - private static _testElementId(index: number): string { - return 'editorSizeProvider' + index; - } - - private static _createTestElements(bareFontInfo: BareFontInfo, readers: CharWidthReader[]): HTMLElement { - let container = document.createElement('div'); - Configuration.applyFontInfoSlow(container, bareFontInfo); - container.style.position = 'absolute'; - container.style.top = '-50000px'; - container.style.width = '50000px'; - - for (let i = 0, len = readers.length; i < len; i++) { - container.appendChild(document.createElement('br')); - - let testElement = document.createElement('span'); - testElement.id = this._testElementId(i); - readers[i].render(testElement); - - container.appendChild(testElement); - } - - container.appendChild(document.createElement('br')); - - return container; - } - - private static _readFromTestElements(readers: CharWidthReader[]): void { - for (let i = 0, len = readers.length; i < len; i++) { - readers[i].read(document.getElementById(this._testElementId(i))); - } - } - - private static _runReaders(bareFontInfo: BareFontInfo, readers: CharWidthReader[]): void { - // Create a test container with all these test elements - let testContainer = this._createTestElements(bareFontInfo, readers); - - // Add the container to the DOM - document.body.appendChild(testContainer); - - // Read various properties - this._readFromTestElements(readers); - - // Remove the container from the DOM - document.body.removeChild(testContainer); - } - private static _actualReadConfiguration(bareFontInfo: BareFontInfo): FontInfo { - let typicalHalfwidthCharacter = new CharWidthReader('n'); - let typicalFullwidthCharacter = new CharWidthReader('\uff4d'); - let space = new CharWidthReader(' '); - let digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].map(chr => new CharWidthReader(chr)); + let canvasElem = document.createElement('canvas'); + let context = canvasElem.getContext('2d'); + context.font = `normal normal normal normal ${bareFontInfo.fontSize}px / ${bareFontInfo.lineHeight}px ${bareFontInfo.fontFamily}`; - this._runReaders(bareFontInfo, digits.concat([typicalHalfwidthCharacter, typicalFullwidthCharacter, space])); + let typicalHalfwidthCharacter = context.measureText('n'); + let typicalFullwidthCharacter = context.measureText('\uff4d'); + let space = context.measureText(' '); + let digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].map(chr => context.measureText(chr)); let maxDigitWidth = 0; for (let i = 0, len = digits.length; i < len; i++) { From f4e88e25df35a084ddad717a19e8927c45fa66e1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 6 Jan 2017 10:43:43 +0100 Subject: [PATCH 107/131] tree.collapse(): forward recursive flag --- src/vs/base/parts/tree/browser/treeImpl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/parts/tree/browser/treeImpl.ts b/src/vs/base/parts/tree/browser/treeImpl.ts index d7453bc6b7b..fe1198623fe 100644 --- a/src/vs/base/parts/tree/browser/treeImpl.ts +++ b/src/vs/base/parts/tree/browser/treeImpl.ts @@ -133,7 +133,7 @@ export class Tree extends Events.EventEmitter implements _.ITree { } public collapse(element: any, recursive: boolean = false): WinJS.Promise { - return this.model.collapse(element); + return this.model.collapse(element, recursive); } public collapseAll(elements: any[] = null, recursive: boolean = false): WinJS.Promise { From ca96c1bdebec2b3bea36f67e9be4be1dc336693b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 6 Jan 2017 10:59:03 +0100 Subject: [PATCH 108/131] remove unused CSS related to #18216 --- .../electron-browser/media/extensions.css | 155 +----------------- 1 file changed, 2 insertions(+), 153 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensions.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensions.css index a0d329f9f8f..8acfffe0567 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensions.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensions.css @@ -3,157 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.quick-open-widget .extension { - padding: 0 14px 0 0; - height: 48px; -} - -.quick-open-widget .extension.loading, -.extensions-viewlet > .extensions .extension.loading, -.extension-editor > .body > .content.loading { - background: url('data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjU3NSIgaGVpZ2h0PSI2cHgiPg0KICA8c3R5bGU+DQogICAgY2lyY2xlIHsNCiAgICAgIGFuaW1hdGlvbjogYmFsbCAyLjVzIGN1YmljLWJlemllcigwLjAwMCwgMS4wMDAsIDEuMDAwLCAwLjAwMCkgaW5maW5pdGU7DQogICAgICBmaWxsOiAjYmJiOw0KICAgIH0NCg0KICAgICNiYWxscyB7DQogICAgICBhbmltYXRpb246IGJhbGxzIDIuNXMgbGluZWFyIGluZmluaXRlOw0KICAgIH0NCg0KICAgICNjaXJjbGUyIHsgYW5pbWF0aW9uLWRlbGF5OiAwLjFzOyB9DQogICAgI2NpcmNsZTMgeyBhbmltYXRpb24tZGVsYXk6IDAuMnM7IH0NCiAgICAjY2lyY2xlNCB7IGFuaW1hdGlvbi1kZWxheTogMC4zczsgfQ0KICAgICNjaXJjbGU1IHsgYW5pbWF0aW9uLWRlbGF5OiAwLjRzOyB9DQoNCiAgICBAa2V5ZnJhbWVzIGJhbGwgew0KICAgICAgZnJvbSB7IHRyYW5zZm9ybTogbm9uZTsgfQ0KICAgICAgMjAlIHsgdHJhbnNmb3JtOiBub25lOyB9DQogICAgICA4MCUgeyB0cmFuc2Zvcm06IHRyYW5zbGF0ZVgoODY0cHgpOyB9DQogICAgICB0byB7IHRyYW5zZm9ybTogdHJhbnNsYXRlWCg4NjRweCk7IH0NCiAgICB9DQoNCiAgICBAa2V5ZnJhbWVzIGJhbGxzIHsNCiAgICAgIGZyb20geyB0cmFuc2Zvcm06IHRyYW5zbGF0ZVgoLTQwcHgpOyB9DQogICAgICB0byB7IHRyYW5zZm9ybTogdHJhbnNsYXRlWCgzMHB4KTsgfQ0KICAgIH0NCiAgPC9zdHlsZT4NCiAgPGcgaWQ9ImJhbGxzIj4NCiAgICA8Y2lyY2xlIGNsYXNzPSJjaXJjbGUiIGlkPSJjaXJjbGUxIiBjeD0iLTExNSIgY3k9IjMiIHI9IjMiLz4NCiAgICA8Y2lyY2xlIGNsYXNzPSJjaXJjbGUiIGlkPSJjaXJjbGUyIiBjeD0iLTEzMCIgY3k9IjMiIHI9IjMiIC8+DQogICAgPGNpcmNsZSBjbGFzcz0iY2lyY2xlIiBpZD0iY2lyY2xlMyIgY3g9Ii0xNDUiIGN5PSIzIiByPSIzIiAvPg0KICAgIDxjaXJjbGUgY2xhc3M9ImNpcmNsZSIgaWQ9ImNpcmNsZTQiIGN4PSItMTYwIiBjeT0iMyIgcj0iMyIgLz4NCiAgICA8Y2lyY2xlIGNsYXNzPSJjaXJjbGUiIGlkPSJjaXJjbGU1IiBjeD0iLTE3NSIgY3k9IjMiIHI9IjMiIC8+DQogIDwvZz4NCjwvc3ZnPg==') center center no-repeat; -} - -.quick-open-widget .extension.loading > * { - opacity: 0.4; -} - -.quick-open-widget .extension .row { - display: block; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - height: 24px; -} - -.quick-open-widget .extension .row .actions { - float: right; -} - -.quick-open-widget .extension .description { - display: block; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - opacity: 0.6; -} - -.quick-open-widget .extension .install { - margin-left: 6px; - padding: 1px 3px; - border-radius: 3px; - background-color: rgba(132, 132, 132, 0.3); - font-size: smaller; - opacity: 0.7; -} - -.quick-open-widget .extension .install > .octicon { - font-size: small; - margin-right: 3px; -} - -.quick-open-widget .extension .icon.octicon-x:before { - margin-right: 2px; - margin-top: 2px; - display: inline-block; -} - -.quick-open-widget .extension .published { - float: right; - opacity: 0.6; - font-size: smaller; -} - -.quick-open-widget .extension .published > .version { - opacity: 0.6; - margin-right: 0.5em; -} - -@keyframes move-background { - to { background-position: 8px 0; } -} - -.quick-open-widget .extension .actions .action-item:not(.disabled) { - display: none; -} - -.quick-open-widget .monaco-tree-row:hover .extension .actions .action-item, -.quick-open-widget .monaco-tree-row.focused .extension .actions .action-item { - display: inherit; -} - -.quick-open-widget .extension .actions .action-item:not(:first-child) { - margin-left: 2px; -} - -.quick-open-widget .extension .actions .action-item { - line-height: 12px; -} - -.quick-open-widget .extension .actions .action-label { - width: 12px; - height: 12px; - font-size: smaller; - border: 1px solid rgba(132, 132, 132, 0.5); - /*border-radius: 2px;*/ - padding: 1px; - vertical-align: text-bottom; - text-transform: uppercase; - color: rgb(0, 157, 255); - border-color: rgb(0, 157, 255); -} - -.quick-open-widget .monaco-tree.focused .monaco-tree-row.focused .extension .actions .action-label:focus { - outline: 1px solid #DF740C; - outline-offset: -1px; -} - -.quick-open-widget .monaco-tree.focused .monaco-tree-row.focused .extension .actions .action-label:active { - outline: none; -} - -.quick-open-widget .extension .actions .action-label:not(.icon) { - padding: 1px 3px; - display: inline-block; - width: auto; - font-size: x-small; -} - -.quick-open-widget .extension .actions .action-item.disabled .action-label { - animation: move-background 0.5s linear infinite; - background-color: rgba(132, 132, 132, 0.5); - background-size: 8px; - background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.5) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0.5) 75%, transparent 75%, transparent); - color: rgb(0, 0, 0); - border-color: rgb(0, 0, 0); -} - -.quick-open-widget .extension .actions .action-item .action-label:hover { - text-decoration: none; -} - -.quick-open-widget .extension .actions .action-item:not(.disabled) .action-label:hover { - color: inherit; - background: rgba(132, 132, 132, 0.2); - border-color: #CCC; -} - -.quick-open-widget .extension .actions .action-item:not(.disabled) .action-label:active { - background: rgba(132, 132, 132, 0.5); -} - -.quick-open-widget .extension .actions .action-item:active { - transform: none; -} - -.monaco-workbench > .activitybar .monaco-action-bar .action-label.extensions { - background: url('extensions-status.svg') center center no-repeat; -} - -/* Global action */ - -.monaco-workbench > .activitybar .monaco-action-bar .action-label.extensions { - background-size: 22px; - background-repeat: no-repeat; - background-position: 50% !important; +.monaco-workbench > .activitybar > .content .monaco-action-bar .action-label.extensions { + background: url('extensions-status.svg') center center/22px no-repeat; } \ No newline at end of file From e0b601ef7bb37a35a68c02c0da2691336ec11298 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 6 Jan 2017 11:04:41 +0100 Subject: [PATCH 109/131] fix sash for #18216 --- src/vs/base/browser/ui/sash/sash.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/sash/sash.css b/src/vs/base/browser/ui/sash/sash.css index 5a2687d5055..0c733246563 100644 --- a/src/vs/base/browser/ui/sash/sash.css +++ b/src/vs/base/browser/ui/sash/sash.css @@ -25,22 +25,22 @@ cursor: default !important; } -.vertical-cursor-container * { +.vertical-cursor-container { cursor: ew-resize; } -.horizontal-cursor-container * { +.horizontal-cursor-container { cursor: ns-resize; } /** Custom Mac Cursor */ .monaco-sash.mac.vertical, -.vertical-cursor-container-mac * { +.vertical-cursor-container-mac { cursor: col-resize; } .monaco-sash.mac.horizontal, -.horizontal-cursor-container-mac * { +.horizontal-cursor-container-mac { cursor: row-resize; } \ No newline at end of file From 8b122cdf817602cf02eea4666aca33aeb4391d0c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 6 Jan 2017 12:16:08 +0100 Subject: [PATCH 110/131] save 16k by not using map. is that true? --- .../platform/instantiation/common/instantiationService.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/instantiation/common/instantiationService.ts b/src/vs/platform/instantiation/common/instantiationService.ts index f4fb6eb0dfb..27ffed19554 100644 --- a/src/vs/platform/instantiation/common/instantiationService.ts +++ b/src/vs/platform/instantiation/common/instantiationService.ts @@ -123,13 +123,15 @@ export class InstantiationService implements IInstantiationService { // arguments defined by service decorators let serviceDependencies = _util.getServiceDependencies(desc.ctor).sort((a, b) => a.index - b.index); - let serviceArgs = serviceDependencies.map(dependency => { + let serviceArgs: any[] = []; + for (const dependency of serviceDependencies) { let service = this._getOrCreateServiceInstance(dependency.id); if (!service && this._strict && !dependency.optional) { throw new Error(`[createInstance] ${desc.ctor.name} depends on UNKNOWN service ${dependency.id}.`); } - return service; - }); + serviceArgs.push(service); + } + let firstServiceArgPos = serviceDependencies.length > 0 ? serviceDependencies[0].index : staticArgs.length; // check for argument mismatches, adjust static args if needed From 3e60335dd42c2a8d95cb7e7c560603177c2583fc Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 6 Jan 2017 12:18:27 +0100 Subject: [PATCH 111/131] debug source: give priority to sourceReference than to paht when determining if internal fixes #16913 --- src/vs/workbench/parts/debug/common/debugSource.ts | 4 ++-- src/vs/workbench/parts/debug/electron-browser/debugViewer.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/debug/common/debugSource.ts b/src/vs/workbench/parts/debug/common/debugSource.ts index df728f73675..e51a24cfefb 100644 --- a/src/vs/workbench/parts/debug/common/debugSource.ts +++ b/src/vs/workbench/parts/debug/common/debugSource.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import uri from 'vs/base/common/uri'; -import * as paths from 'vs/base/common/paths'; import { DEBUG_SCHEME } from 'vs/workbench/parts/debug/common/debug'; export class Source { @@ -15,7 +14,8 @@ export class Source { private static INTERNAL_URI_PREFIX = `${DEBUG_SCHEME}://internal/`; constructor(public raw: DebugProtocol.Source, available = true) { - this.uri = raw.path ? uri.file(paths.normalize(raw.path)) : uri.parse(Source.INTERNAL_URI_PREFIX + raw.sourceReference + '/' + raw.name); + const path = raw.path || raw.name; + this.uri = raw.sourceReference > 0 ? uri.parse(Source.INTERNAL_URI_PREFIX + raw.sourceReference + '/' + path) : uri.file(path); this.available = available; } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts index 576bba7a893..d82164b1452 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts @@ -555,7 +555,7 @@ export class CallStackRenderer implements IRenderer { private renderStackFrame(stackFrame: debug.IStackFrame, data: IStackFrameTemplateData): void { stackFrame.source.available ? dom.removeClass(data.stackFrame, 'disabled') : dom.addClass(data.stackFrame, 'disabled'); - data.file.title = stackFrame.source.uri.fsPath; + data.file.title = stackFrame.source.raw.path || stackFrame.source.name; data.label.textContent = stackFrame.name; data.label.title = stackFrame.name; data.fileName.textContent = getSourceName(stackFrame.source, this.contextService); From 682f5e81deeb43f7906bc91a5a93533a640cc80d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 6 Jan 2017 12:37:06 +0100 Subject: [PATCH 112/131] #18216 Remove universal selectors --- src/vs/workbench/parts/markers/browser/media/markers.css | 4 ---- .../parts/preferences/browser/media/preferences.css | 9 +++++---- .../parts/search/browser/media/searchviewlet.css | 7 ++----- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/parts/markers/browser/media/markers.css b/src/vs/workbench/parts/markers/browser/media/markers.css index 491e4ac3590..2cdcded90d2 100644 --- a/src/vs/workbench/parts/markers/browser/media/markers.css +++ b/src/vs/workbench/parts/markers/browser/media/markers.css @@ -46,10 +46,6 @@ line-height: 22px; } -.markers-panel .markers-panel-container .tree-container .markers-panel-tree-entry > * { - display: inline-block; -} - .markers-panel .markers-panel-container .tree-container .markers-panel-tree-entry .marker-stats { display: inline-block; margin-left: 10px; diff --git a/src/vs/workbench/parts/preferences/browser/media/preferences.css b/src/vs/workbench/parts/preferences/browser/media/preferences.css index 892ba87928d..6c31b27d5e7 100644 --- a/src/vs/workbench/parts/preferences/browser/media/preferences.css +++ b/src/vs/workbench/parts/preferences/browser/media/preferences.css @@ -102,10 +102,6 @@ color: white; } -.monaco-editor .settings-group-title-widget .title-container > * { - vertical-align: middle; - display: inline-block; -} .monaco-editor.vs-dark .settings-group-title-widget .title-container.focused, .monaco-editor.vs .settings-group-title-widget .title-container.focused { @@ -133,6 +129,11 @@ height: 16px; } +.monaco-editor .settings-group-title-widget .title-container > div { + vertical-align: middle; + display: inline-block; +} + .monaco-editor.vs-dark .settings-group-title-widget .title-container .expand-collapse-icon, .monaco-editor.hc-black .settings-group-title-widget .title-container .expand-collapse-icon { background: url(expanded-dark.svg) 50% 50% no-repeat; diff --git a/src/vs/workbench/parts/search/browser/media/searchviewlet.css b/src/vs/workbench/parts/search/browser/media/searchviewlet.css index b4f7cb01317..11a0c709922 100644 --- a/src/vs/workbench/parts/search/browser/media/searchviewlet.css +++ b/src/vs/workbench/parts/search/browser/media/searchviewlet.css @@ -42,18 +42,15 @@ .search-viewlet .search-widget .replace-container { margin-top: 6px; position: relative; + display: inline-flex; + height: 25px; } .search-viewlet .search-widget .replace-container.disabled { display: none; } -.search-viewlet .search-widget .replace-container > * { - display: inline-block; -} - .search-viewlet .search-widget .replace-container .monaco-action-bar { - position: absolute; margin-left: 3px; } From e953f08acf7844f7ccb7b2f7f17eac31469117ad Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 6 Jan 2017 12:57:43 +0100 Subject: [PATCH 113/131] log app.isReady separately because I suspect it to be slow, #18087 --- src/main.js | 1 + src/vs/code/electron-main/window.ts | 2 + src/vs/workbench/electron-browser/actions.ts | 3 +- .../electron-browser/bootstrap/index.js | 1 + .../services/timer/common/timerService.ts | 5 ++- .../services/timer/node/timerService.ts | 39 ++++++++----------- 6 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/main.js b/src/main.js index 289e0e4fb15..d45cc975b8d 100644 --- a/src/main.js +++ b/src/main.js @@ -180,6 +180,7 @@ var nodeCachedDataDir = getNodeCachedDataDir().then(function (value) { // Load our code once ready app.once('ready', function () { + global.perfAppReady = Date.now(); var nlsConfig = getNLSConfiguration(); process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig); diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 56536188d21..dd967a6e14c 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -82,6 +82,7 @@ export interface IWindowConfiguration extends ParsedArgs { isInitialStartup?: boolean; perfStartTime?: number; + perfAppReady?: number; perfWindowLoadTime?: number; workspacePath?: string; @@ -487,6 +488,7 @@ export class VSCodeWindow implements IVSCodeWindow { // Perf Counters windowConfiguration.perfStartTime = global.perfStartTime; + windowConfiguration.perfAppReady = global.perfAppReady; windowConfiguration.perfWindowLoadTime = Date.now(); // Config (combination of process.argv and window configuration) diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 10f15341b35..3c1e03ac623 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -371,7 +371,8 @@ export class ShowStartupPerformance extends Action { const metrics: IStartupMetrics = this.timerService.startupMetrics; if (metrics.initialStartup) { - table.push({ Topic: '[main] start => window.loadUrl()', 'Took (ms)': metrics.timers.ellapsedWindowLoad }); + table.push({ Topic: '[main] start => app.isReady', 'Took (ms)': metrics.timers.ellapsedAppReady }); + table.push({ Topic: '[main] app.isReady => window.loadUrl()', 'Took (ms)': metrics.timers.ellapsedWindowLoad }); } table.push({ Topic: '[renderer] window.loadUrl() => begin to require(workbench.main.js)', 'Took (ms)': metrics.timers.ellapsedWindowLoadToRequire }); diff --git a/src/vs/workbench/electron-browser/bootstrap/index.js b/src/vs/workbench/electron-browser/bootstrap/index.js index b7c60e8f6c2..5d438e137a2 100644 --- a/src/vs/workbench/electron-browser/bootstrap/index.js +++ b/src/vs/workbench/electron-browser/bootstrap/index.js @@ -185,6 +185,7 @@ function main() { isInitialStartup: !!configuration.isInitialStartup, hasAccessibilitySupport: !!configuration.accessibilitySupport, start: new Date(configuration.perfStartTime), + appReady: new Date(configuration.perfAppReady), windowLoad: new Date(configuration.perfWindowLoadTime), beforeLoadWorkbenchMain: new Date() }; diff --git a/src/vs/workbench/services/timer/common/timerService.ts b/src/vs/workbench/services/timer/common/timerService.ts index 8366aa6fe64..19d7c77d7ba 100644 --- a/src/vs/workbench/services/timer/common/timerService.ts +++ b/src/vs/workbench/services/timer/common/timerService.ts @@ -19,6 +19,7 @@ export interface IStartupMetrics { version: number; ellapsed: number; timers: { + ellapsedAppReady?: number; ellapsedWindowLoad?: number; ellapsedWindowLoadToRequire: number; ellapsedExtensions: number; @@ -44,6 +45,8 @@ export interface IStartupMetrics { export interface IInitData { start: Date; + appReady: Date; + windowLoad: Date; beforeLoadWorkbenchMain: Date; @@ -69,4 +72,4 @@ export interface ITimerService extends IInitData { restoreEditorsDuration: number; readonly startupMetrics: IStartupMetrics; -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/timer/node/timerService.ts b/src/vs/workbench/services/timer/node/timerService.ts index a0eb9caa6f1..2b92b832272 100644 --- a/src/vs/workbench/services/timer/node/timerService.ts +++ b/src/vs/workbench/services/timer/node/timerService.ts @@ -13,21 +13,15 @@ export class TimerService implements ITimerService { public _serviceBrand: any; - public get start(): Date { return this._start; } - private _start: Date; + public readonly start: Date; + public readonly appReady: Date; + public readonly windowLoad: Date; - public get windowLoad(): Date { return this._windowLoad; }; - private _windowLoad: Date; + public readonly beforeLoadWorkbenchMain: Date; + public readonly afterLoadWorkbenchMain: Date; - public get beforeLoadWorkbenchMain(): Date { return this._beforeLoadWorkbenchMain; }; - private _beforeLoadWorkbenchMain: Date; - public get afterLoadWorkbenchMain(): Date { return this._afterLoadWorkbenchMain; }; - private _afterLoadWorkbenchMain: Date; - - public get isInitialStartup(): boolean { return this._isInitialStartup; }; - private _isInitialStartup: boolean; - public get hasAccessibilitySupport(): boolean { return this._hasAccessibilitySupport; }; - private _hasAccessibilitySupport: boolean; + public readonly isInitialStartup: boolean; + public readonly hasAccessibilitySupport: boolean; public beforeDOMContentLoaded: Date; public afterDOMContentLoaded: Date; @@ -51,15 +45,15 @@ export class TimerService implements ITimerService { private _startupMetrics: IStartupMetrics; constructor(initData: IInitData, private isEmptyWorkbench: boolean) { - this._start = initData.start; + this.start = initData.start; + this.appReady = initData.appReady; + this.windowLoad = initData.windowLoad; - this._windowLoad = initData.windowLoad; + this.beforeLoadWorkbenchMain = initData.beforeLoadWorkbenchMain; + this.afterLoadWorkbenchMain = initData.afterLoadWorkbenchMain; - this._beforeLoadWorkbenchMain = initData.beforeLoadWorkbenchMain; - this._afterLoadWorkbenchMain = initData.afterLoadWorkbenchMain; - - this._isInitialStartup = initData.isInitialStartup; - this._hasAccessibilitySupport = initData.hasAccessibilitySupport; + this.isInitialStartup = initData.isInitialStartup; + this.hasAccessibilitySupport = initData.hasAccessibilitySupport; // forward start time to time keeper timer.TimeKeeper.PARSE_TIME = initData.isInitialStartup ? initData.start : initData.windowLoad; @@ -120,7 +114,8 @@ export class TimerService implements ITimerService { }; if (initialStartup) { - this._startupMetrics.timers.ellapsedWindowLoad = Math.round(this.windowLoad.getTime() - this.start.getTime()); + this._startupMetrics.timers.ellapsedAppReady = Math.round(this.appReady.getTime() - this.start.getTime()); + this._startupMetrics.timers.ellapsedWindowLoad = Math.round(this.windowLoad.getTime() - this.appReady.getTime()); } } -} \ No newline at end of file +} From a0b6728287f9f963807f1de3b090c191a1e9eada Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Fri, 6 Jan 2017 13:07:07 +0100 Subject: [PATCH 114/131] use debug protocol 1.16.0-pre.0 --- src/vs/workbench/parts/debug/common/debugProtocol.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/parts/debug/common/debugProtocol.d.ts b/src/vs/workbench/parts/debug/common/debugProtocol.d.ts index 790de1fcc91..21c69e61090 100644 --- a/src/vs/workbench/parts/debug/common/debugProtocol.d.ts +++ b/src/vs/workbench/parts/debug/common/debugProtocol.d.ts @@ -1042,11 +1042,11 @@ declare module DebugProtocol { /** A Source is a descriptor for source code. It is returned from the debug adapter as part of a StackFrame and it is used by clients when specifying breakpoints. */ export interface Source { - /** The short name of the source. Every source returned from the debug adapter has a name. When specifying a source to the debug adapter this name is optional. */ + /** The short name of the source. Every source returned from the debug adapter has a name. When sending a source to the debug adapter this name is optional. */ name?: string; - /** The long (absolute) path of the source. It is not guaranteed that the source exists at this location. */ + /** The path of the source to be shown in the UI. It is only used to locate and load the content of the source if no sourceReference is specified (or its vaule is 0). */ path?: string; - /** If sourceReference > 0 the contents of the source can be retrieved through the SourceRequest. A sourceReference is only valid for a session, so it must not be used to persist a source. */ + /** If sourceReference > 0 the contents of the source must be retrieved through the SourceRequest (even if a path is specified). A sourceReference is only valid for a session, so it must not be used to persist a source. */ sourceReference?: number; /** The (optional) origin of this source: possible values 'internal module', 'inlined content from source map', etc. */ origin?: string; @@ -1218,7 +1218,7 @@ declare module DebugProtocol { } /** Some predefined types for the CompletionItem. Please note that not all clients have specific icons for all of them. */ - export type CompletionItemType = 'method' | 'function' | 'constructor' | 'field' | 'variable' | 'class' | 'interface' | 'module' | 'property' | 'unit' | 'value' | 'enum' | 'keyword' | 'snippet' | 'text' | 'color' | 'file' | 'reference' | 'customcolor' | 'folder'; + export type CompletionItemType = 'method' | 'function' | 'constructor' | 'field' | 'variable' | 'class' | 'interface' | 'module' | 'property' | 'unit' | 'value' | 'enum' | 'keyword' | 'snippet' | 'text' | 'color' | 'file' | 'reference' | 'customcolor'; /** Names of checksum algorithms that may be supported by a debug adapter. */ export type ChecksumAlgorithm = 'MD5' | 'SHA1' | 'SHA256' | 'SHA1Normalized' | 'SHA256Normalized' | 'timestamp'; From 01947566303f640e40d9a91d0ea5d34bc0486e49 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 6 Jan 2017 14:51:37 +0100 Subject: [PATCH 115/131] debt - remove unused TimeKeeper --- src/vs/base/browser/ui/timer/timer.css | 29 -- src/vs/base/browser/ui/timer/timer.ts | 281 ----------------- src/vs/base/common/timer.ts | 293 ------------------ src/vs/editor/browser/view/viewImpl.ts | 5 - .../common/model/textModelWithTokens.ts | 4 - src/vs/workbench/electron-browser/main.ts | 7 +- .../parts/search/common/searchModel.ts | 3 - .../search/test/common/searchModel.test.ts | 24 +- .../services/timer/node/timerService.ts | 4 - .../electron-browser/quickopen.perf.test.ts | 18 +- 10 files changed, 40 insertions(+), 628 deletions(-) delete mode 100644 src/vs/base/browser/ui/timer/timer.css delete mode 100644 src/vs/base/browser/ui/timer/timer.ts delete mode 100644 src/vs/base/common/timer.ts diff --git a/src/vs/base/browser/ui/timer/timer.css b/src/vs/base/browser/ui/timer/timer.css deleted file mode 100644 index 9f3515b0d95..00000000000 --- a/src/vs/base/browser/ui/timer/timer.css +++ /dev/null @@ -1,29 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -.benchmarktimerbox { - z-index: 100; - position: absolute; - color: black; - background: lightblue; - top: 100px; - right: 20px; - font-family: monospace; -} - -.benchmarktimerbox .inner { - width: 600px; - height: 300px; - overflow: scroll; -} - -.benchmarktimerbox .timeFilter { - width: 50px; -} - -.benchmarktimerbox pre { - margin: 0; -} - -.timer-event-1 { background: rgba(190, 191, 193, 0.4); color: black; } \ No newline at end of file diff --git a/src/vs/base/browser/ui/timer/timer.ts b/src/vs/base/browser/ui/timer/timer.ts deleted file mode 100644 index 1b19e91e145..00000000000 --- a/src/vs/base/browser/ui/timer/timer.ts +++ /dev/null @@ -1,281 +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!./timer'; -import { TimeKeeper, ITimerEvent, getTimeKeeper } from 'vs/base/common/timer'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import DomUtils = require('vs/base/browser/dom'); - -export class TimeKeeperRenderer { - - private listenersToRemove: IDisposable[]; - private timeKeeper: TimeKeeper; - private outerDomNode: HTMLElement; - private domNode: HTMLElement; - private renderCnt: number; - private lastEventIndex: number; - - private textFilter: string; - private textFilterDomNode: HTMLInputElement; - private timeFilter: number; - private timeFilterDomNode: HTMLInputElement; - private intervalTokenId: number; - - private renderedEvents: { - [key: string]: ITimerEvent; - }; - - private onHide: () => void; - - constructor(onHide: () => void) { - this.timeKeeper = getTimeKeeper(); - this.onHide = onHide; - this.lastEventIndex = 0; - this.renderedEvents = {}; - this.renderCnt = 0; - this.listenersToRemove = []; - this.domNode = this._createDomNode(); - this.intervalTokenId = window.setInterval(() => this._render(), 500); - } - - public destroy(): void { - document.body.removeChild(this.outerDomNode); - window.clearInterval(this.intervalTokenId); - this.listenersToRemove = dispose(this.listenersToRemove); - } - - private _createDomNode(): HTMLElement { - this.outerDomNode = document.createElement('div'); - this.outerDomNode.className = 'benchmarktimerbox'; - - // Clear - let cancel: HTMLInputElement = document.createElement('input'); - cancel.type = 'button'; - cancel.value = 'Clear'; - this.listenersToRemove.push(DomUtils.addDisposableListener(cancel, 'click', () => this._onClear())); - this.outerDomNode.appendChild(cancel); - - // Text filter - this.textFilterDomNode = document.createElement('input'); - this.textFilterDomNode.type = 'text'; - this.textFilterDomNode.className = 'textFilter'; - this.listenersToRemove.push(DomUtils.addDisposableListener(this.textFilterDomNode, 'keydown', () => this.onTextFilterChange())); - this.textFilter = ''; - this.outerDomNode.appendChild(document.createTextNode('Filter')); - this.outerDomNode.appendChild(this.textFilterDomNode); - - // Time filter - this.timeFilterDomNode = document.createElement('input'); - this.timeFilterDomNode.type = 'text'; - this.timeFilterDomNode.value = '0'; - this.timeFilterDomNode.className = 'timeFilter'; - this.listenersToRemove.push(DomUtils.addDisposableListener(this.timeFilterDomNode, 'keydown', () => this.onTimeFilterChange())); - this.timeFilter = 0; - this.outerDomNode.appendChild(document.createTextNode('Hide time under')); - this.outerDomNode.appendChild(this.timeFilterDomNode); - - let hide: HTMLInputElement = document.createElement('input'); - hide.type = 'button'; - hide.value = 'Close'; - this.listenersToRemove.push(DomUtils.addDisposableListener(hide, 'click', () => { - this.onHide(); - })); - this.outerDomNode.appendChild(hide); - - let heading = document.createElement('pre'); - heading.appendChild(document.createTextNode(this.renderRow('TOPIC', 'NAME', 'TOOK', 'START', 'END'))); - this.outerDomNode.appendChild(heading); - this.outerDomNode.appendChild(document.createElement('hr')); - - let domNode = document.createElement('div'); - domNode.className = 'inner'; - this.outerDomNode.appendChild(domNode); - - document.body.appendChild(this.outerDomNode); - - return domNode; - } - - private onTextFilterChange(): void { - setTimeout(() => { - this.refilter(); - }); - } - - private onTimeFilterChange(): void { - setTimeout(() => { - this.refilter(); - }); - } - - private matchesTextFilter(event: ITimerEvent): boolean { - if (!this.textFilter) { - return true; - } - if (event.topic.toLowerCase().indexOf(this.textFilter.toLowerCase()) >= 0) { - return true; - } - if (event.name.toLowerCase().indexOf(this.textFilter.toLowerCase()) >= 0) { - return true; - } - return false; - } - - private matchesTimeFilter(event: ITimerEvent): boolean { - if (!this.timeFilter) { - return true; - } - if (event.timeTaken() >= this.timeFilter) { - return true; - } - return false; - } - - private shouldShow(event: ITimerEvent): boolean { - return this.matchesTextFilter(event) && this.matchesTimeFilter(event); - } - - private refilter(): void { - this.textFilter = this.textFilterDomNode.value; - this.timeFilter = parseInt(this.timeFilterDomNode.value, 10); - - let domNodes = Array.prototype.slice.call(this.domNode.children, 0); - for (let i = 0; i < domNodes.length; i++) { - let eventId = domNodes[i].getAttribute('data-event-id'); - let event = this.renderedEvents[eventId]; - - if (this.shouldShow(event)) { - domNodes[i].style.display = 'inherit'; - } else { - domNodes[i].style.display = 'none'; - } - } - } - - private _onClear(): void { - this.lastEventIndex = this.timeKeeper.getCollectedEvents().length; - this.renderedEvents = {}; - this.renderCnt = 0; - DomUtils.clearNode(this.domNode); - } - - private leftPaddedString(size: number, padChar: string, str: string): string { - let spaces = this._repeatStr(padChar, Math.max(0, size - str.length)); - return spaces + str; - } - - private rightPaddedString(size: number, padChar: string, str: string): string { - let spaces = this._repeatStr(padChar, Math.max(0, size - str.length)); - return str + spaces; - } - - private renderRow(topic: string, name: string, timeTook: string, timeStart: string, timerEnd: string): string { - let result = ' '; - result += this.rightPaddedString(10, ' ', topic); - result += this.rightPaddedString(30, ' ', name); - result += ' ' + this.leftPaddedString(15, ' ', timeTook); - result += ' ' + this.leftPaddedString(13, ' ', timeStart); - return result; - } - - private _suffix0(s: string): string { - if (s.charAt(s.length - 3) === '.') { - return s; - } - if (s.charAt(s.length - 2) === '.') { - return s + '0'; - } - return s + '.00'; - } - - private _twoPrecision(a: number): string { - return this._suffix0(Math.round(a * 100) / 100 + ''); - } - - private _absoluteTime(t: number): string { - if (t < 1000) { - return this._twoPrecision(t) + ' ms'; - } - t /= 1000; - if (t < 60) { - return this._twoPrecision(t) + ' s'; - } - t /= 60; - if (t < 60) { - return this._twoPrecision(t) + ' m'; - } - t /= 60; - return this._twoPrecision(t) + ' h'; - } - - private _renderEvent(domNode: HTMLElement, event: ITimerEvent): void { - let start = event.startTime.getTime() - TimeKeeper.PARSE_TIME.getTime(); - - let result = this.renderRow( - event.topic, - event.name, - this._twoPrecision(event.timeTaken()), - this._absoluteTime(start) + '', - this._absoluteTime(start + event.timeTaken()) - ); - domNode.textContent = ''; - domNode.appendChild(document.createTextNode(result)); - } - - private _renderStartTimerEvent(event: ITimerEvent): void { - let domNode = document.createElement('pre'); - this._renderEvent(domNode, event); - this.domNode.appendChild(domNode); - let idString = event.id.toString(); - - domNode.setAttribute('data-event-id', idString); - domNode.className = 'timer-event-' + (event.id % 2); - this.renderedEvents[idString] = event; - - if (this.shouldShow(this.renderedEvents[idString])) { - domNode.style.display = 'inherit'; - } else { - domNode.style.display = 'none'; - } - - this.renderCnt++; - } - - private _render(): void { - let allEvents = this.timeKeeper.getCollectedEvents(), didSomething = false; - - for (let i = this.lastEventIndex; i < allEvents.length; i++) { - let ev = allEvents[i]; - - if (!ev.stopTime) { - // This event is not yet finished => block - this.lastEventIndex = i; - if (didSomething) { - this.domNode.scrollTop = 100000; - } - return; - } - - this._renderStartTimerEvent(ev); - didSomething = true; - } - - if (didSomething) { - this.domNode.scrollTop = 100000; - } - this.lastEventIndex = allEvents.length; - } - - private _repeatStr(str: string, cnt: number): string { - let r = ''; - for (let i = 0; i < cnt; i++) { - r += str; - } - return r; - } -} - diff --git a/src/vs/base/common/timer.ts b/src/vs/base/common/timer.ts deleted file mode 100644 index 59e00729368..00000000000 --- a/src/vs/base/common/timer.ts +++ /dev/null @@ -1,293 +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 Platform = require('vs/base/common/platform'); -import errors = require('vs/base/common/errors'); -import precision = require('vs/base/common/stopwatch'); -import { IDisposable } from 'vs/base/common/lifecycle'; - -export var ENABLE_TIMER = false; -var msWriteProfilerMark = Platform.globals['msWriteProfilerMark']; - -export enum Topic { - EDITOR, - LANGUAGES, - WORKER, - WORKBENCH, - STARTUP -} - -export interface ITimerEvent { - id: number; - topic: string; - name: string; - description: string; - data: any; - - startTime: Date; - stopTime: Date; - - stop(stopTime?: Date): void; - timeTaken(): number; -} - -export interface IExistingTimerEvent { - topic: string; - name: string; - - description?: string; - - startTime: Date; - stopTime: Date; -} - -class NullTimerEvent implements ITimerEvent { - public id: number; - public topic: string; - public name: string; - public description: string; - public data: any; - - public startTime: Date; - public stopTime: Date; - - public stop(): void { - return; - } - - public timeTaken(): number { - return -1; - } -} - -class TimerEvent implements ITimerEvent { - public id: number; - public topic: string; - public name: string; - public description: string; - public data: any; - - public startTime: Date; - public stopTime: Date; - - private timeKeeper: TimeKeeper; - private sw: precision.StopWatch; - - constructor(timeKeeper: TimeKeeper, name: string, topic: string, startTime?: Date, description?: string) { - this.timeKeeper = timeKeeper; - this.name = name; - this.description = description; - this.topic = topic; - this.stopTime = null; - - if (startTime) { - this.startTime = startTime; - return; - } - - this.startTime = new Date(); - this.sw = precision.StopWatch.create(); - - if (msWriteProfilerMark) { - var profilerName = ['Monaco', this.topic, this.name, 'start']; - msWriteProfilerMark(profilerName.join('|')); - } - } - - public stop(stopTime?: Date): void { - - // already stopped - if (this.stopTime !== null) { - return; - } - - if (stopTime) { - this.stopTime = stopTime; - this.sw = null; - this.timeKeeper._onEventStopped(this); - return; - } - - this.stopTime = new Date(); - if (this.sw) { - this.sw.stop(); - } - - this.timeKeeper._onEventStopped(this); - - if (msWriteProfilerMark) { - var profilerName = ['Monaco', this.topic, this.name, 'stop']; - msWriteProfilerMark(profilerName.join('|')); - } - } - - public timeTaken(): number { - if (this.sw) { - return this.sw.elapsed(); - } - if (this.stopTime) { - return this.stopTime.getTime() - this.startTime.getTime(); - } - return -1; - } -} - -export interface IEventsListener { - (events: ITimerEvent[]): void; -} - -export class TimeKeeper { - /** - * After being started for 1 minute, all timers are automatically stopped. - */ - private static _MAX_TIMER_LENGTH = 60000; // 1 minute - /** - * Every 2 minutes, a sweep of current started timers is done. - */ - private static _CLEAN_UP_INTERVAL = 120000; // 2 minutes - /** - * Collect at most 1000 events. - */ - private static _EVENT_CACHE_LIMIT = 1000; - - private static EVENT_ID = 1; - public static PARSE_TIME = new Date(); - - - private cleaningIntervalId: Platform.IntervalToken; - private collectedEvents: ITimerEvent[]; - private listeners: IEventsListener[]; - - constructor() { - this.cleaningIntervalId = -1; - this.collectedEvents = []; - this.listeners = []; - } - - public isEnabled(): boolean { - return ENABLE_TIMER; - } - - public start(topic: Topic | string, name: string, start?: Date, description?: string): ITimerEvent { - if (!this.isEnabled()) { - return nullEvent; - } - - var strTopic: string; - - if (typeof topic === 'string') { - strTopic = topic; - } else if (topic === Topic.EDITOR) { - strTopic = 'Editor'; - } else if (topic === Topic.LANGUAGES) { - strTopic = 'Languages'; - } else if (topic === Topic.WORKER) { - strTopic = 'Worker'; - } else if (topic === Topic.WORKBENCH) { - strTopic = 'Workbench'; - } else if (topic === Topic.STARTUP) { - strTopic = 'Startup'; - } - - this.initAutoCleaning(); - var event = new TimerEvent(this, name, strTopic, start, description); - this.addEvent(event); - - return event; - } - - public dispose(): void { - if (this.cleaningIntervalId !== -1) { - Platform.clearInterval(this.cleaningIntervalId); - this.cleaningIntervalId = -1; - } - } - - public addListener(listener: IEventsListener): IDisposable { - this.listeners.push(listener); - return { - dispose: () => { - for (var i = 0; i < this.listeners.length; i++) { - if (this.listeners[i] === listener) { - this.listeners.splice(i, 1); - return; - } - } - } - }; - } - - private addEvent(event: ITimerEvent): void { - event.id = TimeKeeper.EVENT_ID; - TimeKeeper.EVENT_ID++; - this.collectedEvents.push(event); - // expire items from the front of the cache - if (this.collectedEvents.length > TimeKeeper._EVENT_CACHE_LIMIT) { - this.collectedEvents.shift(); - } - } - - private initAutoCleaning(): void { - if (this.cleaningIntervalId === -1) { - this.cleaningIntervalId = Platform.setInterval(() => { - var now = Date.now(); - this.collectedEvents.forEach((event) => { - if (!event.stopTime && (now - event.startTime.getTime()) >= TimeKeeper._MAX_TIMER_LENGTH) { - event.stop(); - } - }); - }, TimeKeeper._CLEAN_UP_INTERVAL); - } - } - - public getCollectedEvents(): ITimerEvent[] { - return this.collectedEvents.slice(0); - } - - public clearCollectedEvents(): void { - this.collectedEvents = []; - } - - _onEventStopped(event: ITimerEvent): void { - var emitEvents = [event]; - - var listeners = this.listeners.slice(0); - for (var i = 0; i < listeners.length; i++) { - try { - listeners[i](emitEvents); - } catch (e) { - errors.onUnexpectedError(e); - } - } - } - - public setInitialCollectedEvents(events: IExistingTimerEvent[], startTime?: Date): void { - if (!this.isEnabled()) { - return; - } - - if (startTime) { - TimeKeeper.PARSE_TIME = startTime; - } - - events.forEach((event) => { - var e = new TimerEvent(this, event.name, event.topic, event.startTime, event.description); - e.stop(event.stopTime); - this.addEvent(e); - }); - } -} - -var timeKeeper = new TimeKeeper(); -export var nullEvent: ITimerEvent = new NullTimerEvent(); - -export function start(topic: Topic | string, name: string, start?: Date, description?: string): ITimerEvent { - return timeKeeper.start(topic, name, start, description); -} - -export function getTimeKeeper(): TimeKeeper { - return timeKeeper; -} diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index 4084eb9362d..505fe390815 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -7,7 +7,6 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { EmitterEvent, IEventEmitter } from 'vs/base/common/eventEmitter'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import * as timer from 'vs/base/common/timer'; import * as browser from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; import { StyleMutator } from 'vs/base/browser/styleMutator'; @@ -899,14 +898,12 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp if (!dom.isInDOM(this.domNode)) { return; } - let t = timer.start(timer.Topic.EDITOR, 'View.render'); let viewPartsToRender = this._getViewPartsToRender(); if (!this.viewLines.shouldRender() && viewPartsToRender.length === 0) { // Nothing to render this.keyboardHandler.writeToTextArea(); - t.stop(); return; } @@ -940,8 +937,6 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp // Render the scrollbar this.layoutProvider.renderScrollbar(); - - t.stop(); } private _setHasFocus(newHasFocus: boolean): void { diff --git a/src/vs/editor/common/model/textModelWithTokens.ts b/src/vs/editor/common/model/textModelWithTokens.ts index 3ffa3e17768..f5d1c5c4c9c 100644 --- a/src/vs/editor/common/model/textModelWithTokens.ts +++ b/src/vs/editor/common/model/textModelWithTokens.ts @@ -8,7 +8,6 @@ import * as nls from 'vs/nls'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IDisposable } from 'vs/base/common/lifecycle'; import { StopWatch } from 'vs/base/common/stopwatch'; -import * as timer from 'vs/base/common/timer'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { TextModel } from 'vs/editor/common/model/textModel'; @@ -294,7 +293,6 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke this._withModelTokensChangedEventBuilder((eventBuilder) => { - var t1 = timer.start(timer.Topic.EDITOR, 'backgroundTokenization'); toLineNumber = Math.min(this._lines.length, toLineNumber); var MAX_ALLOWED_TIME = 20, @@ -342,8 +340,6 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke if (this._invalidLineStartIndex < this._lines.length) { this._beginBackgroundTokenization(); } - - t1.stop(); }); } diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index cc391d5aa7d..4f2a20f0373 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -14,7 +14,6 @@ import { domContentLoaded } from 'vs/base/browser/dom'; import errors = require('vs/base/common/errors'); import platform = require('vs/base/common/platform'); import paths = require('vs/base/common/paths'); -import timer = require('vs/base/common/timer'); import uri from 'vs/base/common/uri'; import strings = require('vs/base/common/strings'); import { IResourceInput } from 'vs/platform/editor/common/editor'; @@ -62,10 +61,6 @@ export function startup(configuration: IWindowConfiguration): TPromise { filesToDiff }; - if (configuration.performance) { - timer.ENABLE_TIMER = true; - } - // Resolve workspace return getWorkspace(configuration.workspacePath).then(workspace => { @@ -171,4 +166,4 @@ function loaderError(err: Error): Error { } return new Error(nls.localize('loaderErrorNative', "Failed to load a required file. Please restart the application to try again. Details: {0}", JSON.stringify(err))); -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index 9847b83b1f0..e2ba6cde2ed 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as timer from 'vs/base/common/timer'; import paths = require('vs/base/common/paths'); import objects = require('vs/base/common/objects'); import strings = require('vs/base/common/strings'); @@ -544,14 +543,12 @@ export class SearchModel extends Disposable { this._searchResult.query = this._searchQuery.contentPattern; this._replacePattern = new ReplacePattern(this._replaceString, this._searchQuery.contentPattern); - const timerEvent = timer.start(timer.Topic.WORKBENCH, 'Search'); this.currentRequest = this.searchService.search(this._searchQuery); const onDone = fromPromise(this.currentRequest); const onDoneStopwatch = stopwatch(onDone); const start = Date.now(); - onDone(() => timerEvent.stop()); onDoneStopwatch(duration => this.telemetryService.publicLog('searchResultsFinished', { duration })); const progressEmitter = new Emitter(); diff --git a/src/vs/workbench/parts/search/test/common/searchModel.test.ts b/src/vs/workbench/parts/search/test/common/searchModel.test.ts index 3a5417c1ff8..ca52028f261 100644 --- a/src/vs/workbench/parts/search/test/common/searchModel.test.ts +++ b/src/vs/workbench/parts/search/test/common/searchModel.test.ts @@ -9,7 +9,6 @@ import * as sinon from 'sinon'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { DeferredPPromise } from 'vs/base/test/common/utils'; import { PPromise } from 'vs/base/common/winjs.base'; -import { nullEvent } from 'vs/base/common/timer'; import { SearchModel } from 'vs/workbench/parts/search/common/searchModel'; import URI from 'vs/base/common/uri'; import { IFileMatch, ILineMatch, ISearchService, ISearchComplete, ISearchProgressItem, IUncachedSearchStats } from 'vs/platform/search/common/search'; @@ -20,6 +19,27 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +const nullEvent = new class { + + public id: number; + public topic: string; + public name: string; + public description: string; + public data: any; + + public startTime: Date; + public stopTime: Date; + + public stop(): void { + return; + } + + public timeTaken(): number { + return -1; + } +}; + + suite('SearchModel', () => { let instantiationService: TestInstantiationService; @@ -292,4 +312,4 @@ suite('SearchModel', () => { return instantiationService.createInstance(ModelServiceImpl); } -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/services/timer/node/timerService.ts b/src/vs/workbench/services/timer/node/timerService.ts index 2b92b832272..ffec104d14e 100644 --- a/src/vs/workbench/services/timer/node/timerService.ts +++ b/src/vs/workbench/services/timer/node/timerService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import * as timer from 'vs/base/common/timer'; import { ITimerService, IStartupMetrics, IInitData, IMemoryInfo } from 'vs/workbench/services/timer/common/timerService'; import * as os from 'os'; @@ -54,9 +53,6 @@ export class TimerService implements ITimerService { this.isInitialStartup = initData.isInitialStartup; this.hasAccessibilitySupport = initData.hasAccessibilitySupport; - - // forward start time to time keeper - timer.TimeKeeper.PARSE_TIME = initData.isInitialStartup ? initData.start : initData.windowLoad; } public computeStartupMetrics(): void { diff --git a/src/vs/workbench/test/electron-browser/quickopen.perf.test.ts b/src/vs/workbench/test/electron-browser/quickopen.perf.test.ts index 52845c580bb..1d325306e0a 100644 --- a/src/vs/workbench/test/electron-browser/quickopen.perf.test.ts +++ b/src/vs/workbench/test/electron-browser/quickopen.perf.test.ts @@ -22,7 +22,6 @@ import { SearchService } from 'vs/workbench/services/search/node/searchService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { TestEnvironmentService, TestEditorService, TestEditorGroupService } from 'vs/workbench/test/workbenchTestServices'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import * as Timer from 'vs/base/common/timer'; import { TPromise } from 'vs/base/common/winjs.base'; import URI from 'vs/base/common/uri'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; @@ -31,6 +30,23 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { IModelService } from 'vs/editor/common/services/modelService'; + +namespace Timer { + export interface ITimerEvent { + id: number; + topic: string; + name: string; + description: string; + data: any; + + startTime: Date; + stopTime: Date; + + stop(stopTime?: Date): void; + timeTaken(): number; + } +} + declare var __dirname: string; // Checkout sources to run against: From d00f346f82d2c2e582b02c8d316298b1aede53a6 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Fri, 6 Jan 2017 15:29:42 +0100 Subject: [PATCH 116/131] update node-debug --- build/gulpfile.vscode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 846b10ecdce..5ecb9a1f315 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -39,7 +39,7 @@ const nodeModules = ['electron', 'original-fs'] // Build const builtInExtensions = [ - { name: 'ms-vscode.node-debug', version: '1.9.0' }, + { name: 'ms-vscode.node-debug', version: '1.9.1' }, { name: 'ms-vscode.node-debug2', version: '1.9.1' } ]; From 35f737d1c3c783245cc958e206c819272bacbe01 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 6 Jan 2017 15:59:39 +0100 Subject: [PATCH 117/131] debt - simplify how editor config gets applied --- .../common/config/commonEditorConfig.ts | 46 +------------------ src/vs/platform/files/common/files.ts | 6 +-- .../browser/parts/editor/textEditor.ts | 39 ++++++++-------- 3 files changed, 26 insertions(+), 65 deletions(-) diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 11f9a39f1b1..dfd9904fa86 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -598,50 +598,8 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed protected abstract readConfiguration(styling: editorCommon.BareFontInfo): editorCommon.FontInfo; } -/** - * Helper to update Monaco Editor Settings from configurations service. - */ -export class EditorConfiguration { - public static EDITOR_SECTION = 'editor'; - public static DIFF_EDITOR_SECTION = 'diffEditor'; - - /** - * Ask the provided configuration service to apply its configuration to the provided editor. - */ - public static apply(config: any, editor: editorCommon.IEditor): void { - if (!config) { - return; - } - - // Editor Settings (Code Editor, Diff, Terminal) - if (editor && typeof editor.updateOptions === 'function') { - let type = editor.getEditorType(); - if (type !== editorCommon.EditorType.ICodeEditor && type !== editorCommon.EditorType.IDiffEditor) { - return; - } - - let editorConfig = config[EditorConfiguration.EDITOR_SECTION]; - if (type === editorCommon.EditorType.IDiffEditor) { - let diffEditorConfig = config[EditorConfiguration.DIFF_EDITOR_SECTION]; - if (diffEditorConfig) { - if (!editorConfig) { - editorConfig = diffEditorConfig; - } else { - editorConfig = objects.mixin(editorConfig, diffEditorConfig); - } - } - } - - if (editorConfig) { - delete editorConfig.readOnly; // Prevent someone from making editor readonly - editor.updateOptions(editorConfig); - } - } - } -} - -let configurationRegistry = Registry.as(Extensions.Configuration); -let editorConfiguration: IConfigurationNode = { +const configurationRegistry = Registry.as(Extensions.Configuration); +const editorConfiguration: IConfigurationNode = { 'id': 'editor', 'order': 5, 'type': 'object', diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index e3e072b288e..37f5a52df21 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -223,7 +223,7 @@ export class FileChangesEvent extends events.Event { return false; } - return this._changes.some((change) => { + return this._changes.some(change => { if (change.type !== type) { return false; } @@ -280,11 +280,11 @@ export class FileChangesEvent extends events.Event { } private getOfType(type: FileChangeType): IFileChange[] { - return this._changes.filter((change) => change.type === type); + return this._changes.filter(change => change.type === type); } private hasType(type: FileChangeType): boolean { - return this._changes.some((change) => { + return this._changes.some(change => { return change.type === type; }); } diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index d9747208de9..18a16159854 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -9,13 +9,12 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { Dimension, Builder } from 'vs/base/browser/builder'; import objects = require('vs/base/common/objects'); import errors = require('vs/base/common/errors'); +import types = require('vs/base/common/types'); import DOM = require('vs/base/browser/dom'); import { CodeEditor } from 'vs/editor/browser/codeEditor'; import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { EditorConfiguration } from 'vs/editor/common/config/commonEditorConfig'; -import { IEditorViewState, IEditor, IEditorOptions, EventType as EditorEventType } from 'vs/editor/common/editorCommon'; -import { IFilesConfiguration } from 'vs/platform/files/common/files'; +import { IEditorViewState, IEditor, IEditorOptions, EventType as EditorEventType, EditorType } from 'vs/editor/common/editorCommon'; import { Position } from 'vs/platform/editor/common/editor'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -34,6 +33,11 @@ interface ITextEditorViewState { 2?: IEditorViewState; } +interface IEditorConfiguration { + editor: any; + diffEditor: any; +} + /** * The base class of editors that leverage the text editor for the editing experience. This class is only intended to * be subclassed and not instantiated. @@ -56,14 +60,14 @@ export abstract class BaseTextEditor extends BaseEditor { super(id, telemetryService); this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.handleConfigurationChangeEvent(e.config))); - this.toUnbind.push(themeService.onDidColorThemeChange(_ => this.handleConfigurationChangeEvent())); + this.toUnbind.push(themeService.onDidColorThemeChange(e => this.handleConfigurationChangeEvent(this.configurationService.getConfiguration()))); } protected get instantiationService(): IInstantiationService { return this._instantiationService; } - private handleConfigurationChangeEvent(configuration?: any): void { + private handleConfigurationChangeEvent(configuration: IEditorConfiguration): void { if (this.isVisible()) { this.applyConfiguration(configuration); } else { @@ -73,28 +77,27 @@ export abstract class BaseTextEditor extends BaseEditor { private consumePendingConfigurationChangeEvent(): void { if (this.hasPendingConfigurationChange) { - this.applyConfiguration(this.configurationService.getConfiguration()); + this.applyConfiguration(this.configurationService.getConfiguration()); this.hasPendingConfigurationChange = false; } } - protected applyConfiguration(configuration?: any): void { + private applyConfiguration(configuration: IEditorConfiguration): void { if (!this.editorControl) { return; } - // Configuration & Options - if (configuration) { - const specificEditorSettings = this.getCodeEditorOptions(); - configuration = objects.clone(configuration); // dont modify original config - objects.assign(configuration[EditorConfiguration.EDITOR_SECTION], specificEditorSettings); - EditorConfiguration.apply(configuration, this.editorControl); + // Specific editor options always overwrite user configuration + const editorConfiguration = types.isObject(configuration.editor) ? objects.clone(configuration.editor) : Object.create(null); + objects.assign(editorConfiguration, this.getCodeEditorOptions()); + + // Handle diff editor specially by merging in diffEditor configuration + if (this.editorControl.getEditorType() === EditorType.IDiffEditor && types.isObject(configuration.diffEditor)) { + objects.mixin(editorConfiguration, configuration.diffEditor); } - // Just options - else { - this.editorControl.updateOptions(this.getCodeEditorOptions()); - } + // Apply to control + this.editorControl.updateOptions(editorConfiguration); } protected getCodeEditorOptions(): IEditorOptions { @@ -119,7 +122,7 @@ export abstract class BaseTextEditor extends BaseEditor { this.toUnbind.push(DOM.addDisposableListener(window, DOM.EventType.BLUR, () => this.onWindowFocusLost())); // Configuration - this.applyConfiguration(this.configurationService.getConfiguration()); + this.applyConfiguration(this.configurationService.getConfiguration()); } private onEditorFocusLost(): void { From 833bc88df8f1887516cbcf91cc36f0e2d541b00b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 6 Jan 2017 07:34:21 -0800 Subject: [PATCH 118/131] Remove universal selector Fixes #18216 --- .../parts/terminal/electron-browser/media/xterm.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/xterm.css b/src/vs/workbench/parts/terminal/electron-browser/media/xterm.css index f27caed5b94..f6c97a0c0b4 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/media/xterm.css +++ b/src/vs/workbench/parts/terminal/electron-browser/media/xterm.css @@ -181,17 +181,17 @@ /* Base selection colors */ -.monaco-workbench .panel.integrated-terminal .xterm *::selection { +.monaco-workbench .panel.integrated-terminal .xterm ::selection { color: #FFF; background-color: rgba(51, 51, 51, 0.996); } -.vs-dark .monaco-workbench .panel.integrated-terminal .xterm *::selection { +.vs-dark .monaco-workbench .panel.integrated-terminal .xterm ::selection { color: #1e1e1e; background-color: rgba(204, 204, 204, 0.996); } -.hc-black .monaco-workbench .panel.integrated-terminal .xterm *::selection { +.hc-black .monaco-workbench .panel.integrated-terminal .xterm ::selection { color: #000; background-color: rgba(255, 255, 255, 0.996); } From 27a9144e45e16894d0c37b33085f27bba4b9bf35 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 6 Jan 2017 17:45:41 +0100 Subject: [PATCH 119/131] Fix #17543 --- .../parts/preferences/browser/preferencesEditor.ts | 1 - .../parts/preferences/browser/preferencesWidgets.ts | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index bc7a744c14f..96c0a7da7ba 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -142,7 +142,6 @@ export class DefaultPreferencesEditor extends BaseTextEditor { options.folding = false; options.renderWhitespace = 'none'; options.wrappingColumn = 0; - options.overviewRulerLanes = 0; options.renderIndentGuides = false; options.rulers = []; } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts index 8bbd3b3911d..a6260610aec 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -91,8 +91,11 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone { } private layout(): void { - this.titleContainer.style.lineHeight = this.editor.getConfiguration().lineHeight + 3 + 'px'; - this.titleContainer.style.fontSize = this.editor.getConfiguration().fontInfo.fontSize + 'px'; + const configuration = this.editor.getConfiguration(); + const layoutInfo = this.editor.getLayoutInfo(); + this.titleContainer.style.width = layoutInfo.contentWidth - layoutInfo.verticalScrollbarWidth + 'px'; + this.titleContainer.style.lineHeight = configuration.lineHeight + 3 + 'px'; + this.titleContainer.style.fontSize = configuration.fontInfo.fontSize + 'px'; const iconSize = this.getIconSize(); this.icon.style.height = `${iconSize}px`; this.icon.style.width = `${iconSize}px`; From d4f4281739d9219bb40f1a8ddbc4c6dfd6ce5331 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 6 Jan 2017 17:49:59 +0100 Subject: [PATCH 120/131] Fix #18155 --- .../parts/extensions/node/extensionsWorkbenchService.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 2cda12aa0b4..64e21cd4d8d 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -580,11 +580,10 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { .then(null, onUnexpectedError); } } - } - - if (extension.gallery) { - // Report telemetry only for gallery extensions - this.reportTelemetry(installing, !error); + if (extension.gallery) { + // Report telemetry only for gallery extensions + this.reportTelemetry(installing, !error); + } } this._onChange.fire(); } From aff393e30278862b207337b3e4d5158c0ffaf78a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 6 Jan 2017 17:56:45 +0100 Subject: [PATCH 121/131] Fix #18164 --- .../workbench/parts/preferences/browser/preferencesService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts index f3e53ff1119..24d871f3a58 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -125,7 +125,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic openWorkspaceSettings(): TPromise { if (!this.contextService.hasWorkspace()) { this.messageService.show(Severity.Info, nls.localize('openFolderFirst', "Open a folder first to create workspace settings")); - return; + return TPromise.as(null); } return this.openSettings(ConfigurationTarget.WORKSPACE); } From daad4cf67dfaa4424c5a98ca05c8533e6eb1b625 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 6 Jan 2017 18:19:07 +0100 Subject: [PATCH 122/131] fixes #18166 --- src/vs/workbench/parts/debug/common/debugModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index 9a6b23de05b..5b97853ef25 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -520,7 +520,7 @@ export class Process implements debug.IProcess { thread.stopped = true; thread.clearCallStack(); }); - } else { + } else if (this.threads.has(data.threadId)) { // One thread is stopped, only update that thread. const thread = this.threads.get(data.threadId); thread.stoppedDetails = data.stoppedDetails; From d8048efec2e372460d5455e0929f81b2ac8310e2 Mon Sep 17 00:00:00 2001 From: Wade Anderson Date: Fri, 6 Jan 2017 10:43:26 -0800 Subject: [PATCH 123/131] Added download links for stable and insiders --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 91566a493db..5d5ee55c63a 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ a code editor with what developers need for their core edit-build-debug cycle. Code provides comprehensive editing and debugging support, an extensibility model, and lightweight integration with existing tools. +VS Code is updated monthly with new features and bug fixes. You can download it for Windows, Mac and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases everyday, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated at least daily. +

VS Code in action

From cf1852ee3264103594a699be5454afdca0ac2fff Mon Sep 17 00:00:00 2001 From: roblou Date: Fri, 6 Jan 2017 10:48:21 -0800 Subject: [PATCH 124/131] Fix #18236 - need to also focus the selected element (even though the editor will still have focus) --- src/vs/workbench/parts/search/browser/searchViewlet.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts index 748b1904ff2..1fcd97fd773 100644 --- a/src/vs/workbench/parts/search/browser/searchViewlet.ts +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -470,6 +470,7 @@ export class SearchViewlet extends Viewlet { } // Reveal the newly selected element + this.tree.setFocus(next, eventPayload); this.tree.setSelection([next], eventPayload); this.tree.reveal(next); } @@ -497,7 +498,9 @@ export class SearchViewlet extends Viewlet { } // Reveal the newly selected element + this.tree.setFocus(prev, eventPayload); this.tree.setSelection([prev], eventPayload); + this.tree.reveal(prev); } public setVisible(visible: boolean): TPromise { From 8f0e7bd56fc66ad7178d9a321b534b050ee3cf06 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 6 Jan 2017 10:55:15 -0800 Subject: [PATCH 125/131] Fix terminal font scale when zoomed Fixes #14479 --- .../electron-browser/terminalConfigHelper.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts index 113024a23db..1cca913569e 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IConfiguration, DefaultConfig } from 'vs/editor/common/config/defaultConfig'; +import { IConfiguration as IEditorConfiguration, DefaultConfig } from 'vs/editor/common/config/defaultConfig'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITerminalConfiguration, ITerminalConfigHelper, ITerminalFont, IShell } from 'vs/workbench/parts/terminal/common/terminal'; import { Platform } from 'vs/base/common/platform'; @@ -91,16 +91,16 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { this._charMeasureElement = document.createElement('div'); this.panelContainer.appendChild(this._charMeasureElement); } - let style = this._charMeasureElement.style; + const style = this._charMeasureElement.style; style.display = 'block'; style.fontFamily = fontFamily; style.fontSize = fontSize + 'px'; - style.height = Math.floor(lineHeight * fontSize) + 'px'; + style.lineHeight = lineHeight.toString(10); this._charMeasureElement.innerText = 'X'; - let rect = this._charMeasureElement.getBoundingClientRect(); + const rect = this._charMeasureElement.getBoundingClientRect(); style.display = 'none'; - let charWidth = Math.ceil(rect.width); - let charHeight = Math.ceil(rect.height); + const charWidth = Math.ceil(rect.width); + const charHeight = Math.ceil(rect.height); return { fontFamily, fontSize: fontSize + 'px', @@ -115,10 +115,11 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { * terminal.integrated.fontSize, terminal.integrated.lineHeight configuration properties */ public getFont(): ITerminalFont { - let terminalConfig = this._configurationService.getConfiguration().terminal.integrated; - let editorConfig = this._configurationService.getConfiguration(); + const config = this._configurationService.getConfiguration(); + const editorConfig = (config).editor; + const terminalConfig = (config).terminal.integrated; - let fontFamily = terminalConfig.fontFamily || editorConfig.editor.fontFamily; + let fontFamily = terminalConfig.fontFamily || editorConfig.fontFamily; let fontSize = this._toInteger(terminalConfig.fontSize, 0); if (fontSize <= 0) { fontSize = DefaultConfig.editor.fontSize; From 88168dab0718c8dead911101511fe1e0ef76e8d2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 6 Jan 2017 10:57:15 -0800 Subject: [PATCH 126/131] Use const in terminalInstance --- .../electron-browser/terminalInstance.ts | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 2e56d1240c9..329ad48c685 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -156,8 +156,8 @@ export class TerminalInstance implements ITerminalInstance { }, 0); }); - let xtermHelper: HTMLElement = this._xterm.element.querySelector('.xterm-helpers'); - let focusTrap: HTMLElement = document.createElement('div'); + const xtermHelper: HTMLElement = this._xterm.element.querySelector('.xterm-helpers'); + const focusTrap: HTMLElement = document.createElement('div'); focusTrap.setAttribute('tabindex', '0'); DOM.addClass(focusTrap, 'focus-trap'); focusTrap.addEventListener('focus', function (event: FocusEvent) { @@ -165,7 +165,7 @@ export class TerminalInstance implements ITerminalInstance { while (!DOM.hasClass(currentElement, 'part')) { currentElement = currentElement.parentElement; } - let hidePanelElement = currentElement.querySelector('.hide-panel-action'); + const hidePanelElement = currentElement.querySelector('.hide-panel-action'); hidePanelElement.focus(); }); xtermHelper.insertBefore(focusTrap, this._xterm.textarea); @@ -240,7 +240,7 @@ export class TerminalInstance implements ITerminalInstance { if (!this._xterm) { return; } - let text = window.getSelection().toString(); + const text = window.getSelection().toString(); if (!text || force) { this._xterm.focus(); } @@ -331,11 +331,11 @@ export class TerminalInstance implements ITerminalInstance { } protected _createProcess(workspace: IWorkspace, name: string, shell: IShell) { - let locale = this._configHelper.isSetLocaleVariables() ? platform.locale : undefined; + const locale = this._configHelper.isSetLocaleVariables() ? platform.locale : undefined; if (!shell.executable) { shell = this._configHelper.getShell(); } - let env = TerminalInstance.createTerminalEnv(process.env, shell, this._getCwd(workspace, shell.ignoreCustomCwd), locale); + const env = TerminalInstance.createTerminalEnv(process.env, shell, this._getCwd(workspace, shell.ignoreCustomCwd), locale); this._title = name ? name : ''; this._process = cp.fork('./terminalProcess', [], { env: env, @@ -378,7 +378,7 @@ export class TerminalInstance implements ITerminalInstance { // TODO: This should be private/protected // TODO: locale should not be optional public static createTerminalEnv(parentEnv: IStringDictionary, shell: IShell, cwd: string, locale?: string): IStringDictionary { - let env = TerminalInstance._cloneEnv(parentEnv); + const env = TerminalInstance._cloneEnv(parentEnv); env['PTYPID'] = process.pid.toString(); env['PTYSHELL'] = shell.executable; if (shell.args) { @@ -402,7 +402,7 @@ export class TerminalInstance implements ITerminalInstance { } private static _cloneEnv(env: IStringDictionary): IStringDictionary { - let newEnv: IStringDictionary = Object.create(null); + const newEnv: IStringDictionary = Object.create(null); Object.keys(env).forEach((key) => { newEnv[key] = env[key]; }); @@ -442,7 +442,7 @@ export class TerminalInstance implements ITerminalInstance { } public layout(dimension: { width: number, height: number }): void { - let font = this._configHelper.getFont(); + const font = this._configHelper.getFont(); if (!font || !font.charWidth || !font.charHeight) { return; } @@ -455,10 +455,10 @@ export class TerminalInstance implements ITerminalInstance { // Upstream issue: https://github.com/sourcelair/xterm.js/issues/291 this._xterm.emit('scroll', this._xterm.ydisp); } - let leftPadding = parseInt(getComputedStyle(document.querySelector('.terminal-outer-container')).paddingLeft.split('px')[0], 10); - let innerWidth = dimension.width - leftPadding; - let cols = Math.floor(innerWidth / font.charWidth); - let rows = Math.floor(dimension.height / font.charHeight); + const leftPadding = parseInt(getComputedStyle(document.querySelector('.terminal-outer-container')).paddingLeft.split('px')[0], 10); + const innerWidth = dimension.width - leftPadding; + const cols = Math.floor(innerWidth / font.charWidth); + const rows = Math.floor(dimension.height / font.charHeight); if (this._xterm) { this._xterm.resize(cols, rows); this._xterm.element.style.width = innerWidth + 'px'; From 722546a55239a6f8aaa9ec000405222614e0d679 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 6 Jan 2017 11:01:49 -0800 Subject: [PATCH 127/131] Fill up available space in terminal, add padding to right Fixes #18237 --- .../parts/terminal/electron-browser/terminalConfigHelper.ts | 4 ++-- .../parts/terminal/electron-browser/terminalInstance.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts index 1cca913569e..1d0411b43f0 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts @@ -99,8 +99,8 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { this._charMeasureElement.innerText = 'X'; const rect = this._charMeasureElement.getBoundingClientRect(); style.display = 'none'; - const charWidth = Math.ceil(rect.width); - const charHeight = Math.ceil(rect.height); + const charWidth = rect.width; + const charHeight = rect.height; return { fontFamily, fontSize: fontSize + 'px', diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 329ad48c685..b6872ffdd47 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -455,8 +455,8 @@ export class TerminalInstance implements ITerminalInstance { // Upstream issue: https://github.com/sourcelair/xterm.js/issues/291 this._xterm.emit('scroll', this._xterm.ydisp); } - const leftPadding = parseInt(getComputedStyle(document.querySelector('.terminal-outer-container')).paddingLeft.split('px')[0], 10); - const innerWidth = dimension.width - leftPadding; + const padding = parseInt(getComputedStyle(document.querySelector('.terminal-outer-container')).paddingLeft.split('px')[0], 10); + const innerWidth = dimension.width - padding * 2; // Use left padding as right padding const cols = Math.floor(innerWidth / font.charWidth); const rows = Math.floor(dimension.height / font.charHeight); if (this._xterm) { From f7a1766e3332c30351bd023d5824c98c1198f853 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 6 Jan 2017 11:07:14 -0800 Subject: [PATCH 128/131] Clarify comment --- .../parts/terminal/electron-browser/terminalInstance.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index b6872ffdd47..f2b76b3f41c 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -456,7 +456,9 @@ export class TerminalInstance implements ITerminalInstance { this._xterm.emit('scroll', this._xterm.ydisp); } const padding = parseInt(getComputedStyle(document.querySelector('.terminal-outer-container')).paddingLeft.split('px')[0], 10); - const innerWidth = dimension.width - padding * 2; // Use left padding as right padding + // Use left padding as right padding, right padding is not defined in CSS just in case + // xterm.js causes an unexpected overflow. + const innerWidth = dimension.width - padding * 2; const cols = Math.floor(innerWidth / font.charWidth); const rows = Math.floor(dimension.height / font.charHeight); if (this._xterm) { From 33b891676b78412194e21751045bb7912f85aba6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 6 Jan 2017 11:09:36 -0800 Subject: [PATCH 129/131] Fix tests --- .../parts/terminal/electron-browser/terminalConfigHelper.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts index 1d0411b43f0..ea388837ada 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts @@ -125,6 +125,9 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { fontSize = DefaultConfig.editor.fontSize; } let lineHeight = terminalConfig.lineHeight <= 0 ? DEFAULT_LINE_HEIGHT : terminalConfig.lineHeight; + if (!lineHeight) { + lineHeight = DEFAULT_LINE_HEIGHT; + } return this._measureFont(fontFamily, fontSize, lineHeight); } From f14e8ed4678332d72cef8076c22fa6907820d832 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 6 Jan 2017 11:19:13 -0800 Subject: [PATCH 130/131] Use const in terminalConfigHelper --- .../electron-browser/terminalConfigHelper.ts | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts index ea388837ada..7d05f7e29aa 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts @@ -119,7 +119,7 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { const editorConfig = (config).editor; const terminalConfig = (config).terminal.integrated; - let fontFamily = terminalConfig.fontFamily || editorConfig.fontFamily; + const fontFamily = terminalConfig.fontFamily || editorConfig.fontFamily; let fontSize = this._toInteger(terminalConfig.fontSize, 0); if (fontSize <= 0) { fontSize = DefaultConfig.editor.fontSize; @@ -133,23 +133,28 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { } public getFontLigaturesEnabled(): boolean { - let terminalConfig = this._configurationService.getConfiguration().terminal.integrated; - return terminalConfig.fontLigatures; + const terminalConfig = this._configurationService.getConfiguration(); + return terminalConfig.terminal.integrated.fontLigatures; } public getCursorBlink(): boolean { - let terminalConfig = this._configurationService.getConfiguration().terminal.integrated; - return terminalConfig.cursorBlinking; + const terminalConfig = this._configurationService.getConfiguration(); + return terminalConfig.terminal.integrated.cursorBlinking; } public getRightClickCopyPaste(): boolean { - let config = this._configurationService.getConfiguration(); + const config = this._configurationService.getConfiguration(); return config.terminal.integrated.rightClickCopyPaste; } + public getCommandsToSkipShell(): string[] { + const config = this._configurationService.getConfiguration(); + return config.terminal.integrated.commandsToSkipShell; + } + public getShell(): IShell { - let config = this._configurationService.getConfiguration(); - let shell: IShell = { + const config = this._configurationService.getConfiguration(); + const shell: IShell = { executable: '', args: [] }; @@ -194,9 +199,4 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { } return r; } - - public getCommandsToSkipShell(): string[] { - let config = this._configurationService.getConfiguration(); - return config.terminal.integrated.commandsToSkipShell; - } } \ No newline at end of file From 265803b0c9acd050b00983ad779519774784d58c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 6 Jan 2017 15:18:06 -0800 Subject: [PATCH 131/131] Add TypeScript References Code Lens Provider (#18205) Fixes #18054 Adds an references code lens provide for JS and TS --- extensions/typescript/package.json | 5 + extensions/typescript/package.nls.json | 3 +- .../features/referencesCodeLensProvider.ts | 144 ++++++++++++++++++ extensions/typescript/src/typescriptMain.ts | 12 ++ 4 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 extensions/typescript/src/features/referencesCodeLensProvider.ts diff --git a/extensions/typescript/package.json b/extensions/typescript/package.json index 201ddf039de..9f30f48b4f4 100644 --- a/extensions/typescript/package.json +++ b/extensions/typescript/package.json @@ -99,6 +99,11 @@ "default": true, "description": "%typescript.check.tscVersion%" }, + "typescript.referencesCodeLens.enabled": { + "type": "boolean", + "default": false, + "description": "%typescript.referencesCodeLens.enabled%" + }, "typescript.tsserver.trace": { "type": "string", "enum": [ diff --git a/extensions/typescript/package.nls.json b/extensions/typescript/package.nls.json index 51b9649ab91..1834876a6d9 100644 --- a/extensions/typescript/package.nls.json +++ b/extensions/typescript/package.nls.json @@ -24,5 +24,6 @@ "format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "Defines space handling after opening and before closing JSX expression braces. Requires TypeScript >= 2.0.6.", "format.placeOpenBraceOnNewLineForFunctions": "Defines whether an open brace is put onto a new line for functions or not.", "format.placeOpenBraceOnNewLineForControlBlocks": "Defines whether an open brace is put onto a new line for control blocks or not.", - "javascript.validate.enable": "Enable/disable JavaScript validation." + "javascript.validate.enable": "Enable/disable JavaScript validation.", + "typescript.referencesCodeLens.enabled": "Enable/disable the references code lens" } \ No newline at end of file diff --git a/extensions/typescript/src/features/referencesCodeLensProvider.ts b/extensions/typescript/src/features/referencesCodeLensProvider.ts new file mode 100644 index 00000000000..145859cecb2 --- /dev/null +++ b/extensions/typescript/src/features/referencesCodeLensProvider.ts @@ -0,0 +1,144 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { CodeLensProvider, CodeLens, CancellationToken, TextDocument, Range, Uri, Location, Position, workspace, WorkspaceConfiguration } from 'vscode'; +import * as Proto from '../protocol'; +import * as PConst from '../protocol.const'; + +import { ITypescriptServiceClient } from '../typescriptService'; + +import * as nls from 'vscode-nls'; +let localize = nls.loadMessageBundle(); + + +class ReferencesCodeLens extends CodeLens { + public document: Uri; + public file: string; + + constructor(document: Uri, file: string, range: Range) { + super(range); + this.document = document; + this.file = file; + } +} + +export default class TypeScriptReferencesCodeLensProvider implements CodeLensProvider { + private client: ITypescriptServiceClient; + private enabled = false; + + constructor(client: ITypescriptServiceClient) { + this.client = client; + } + + public updateConfiguration(config: WorkspaceConfiguration): void { + let typeScriptConfig = workspace.getConfiguration('typescript'); + this.enabled = typeScriptConfig.get('referencesCodeLens.enabled', false); + } + + provideCodeLenses(document: TextDocument, token: CancellationToken): Promise { + if (!this.enabled) { + return Promise.resolve([]); + } + + const filepath = this.client.asAbsolutePath(document.uri); + if (!filepath) { + return Promise.resolve([]); + } + return this.client.execute('navtree', { file: filepath }, token).then(response => { + const tree = response.body; + const referenceableSpans: Range[] = []; + if (tree && tree.childItems) { + tree.childItems.forEach(item => this.extractReferenceableSymbols(document, item, referenceableSpans)); + } + return Promise.resolve(referenceableSpans.map(span => new ReferencesCodeLens(document.uri, filepath, span))); + }); + } + + resolveCodeLens(inputCodeLens: CodeLens, token: CancellationToken): Promise { + const codeLens = inputCodeLens as ReferencesCodeLens; + if (!codeLens.document) { + return Promise.reject(codeLens); + } + const args: Proto.FileLocationRequestArgs = { + file: codeLens.file, + line: codeLens.range.start.line + 1, + offset: codeLens.range.start.character + 1 + }; + return this.client.execute('references', args, token).then(response => { + if (response && response.body) { + const referenceCount = Math.max(0, response.body.refs.length - 1); + const locations = response.body.refs.map(reference => + new Location(Uri.file(reference.file), + new Range( + new Position(reference.start.line - 1, reference.start.offset - 1), + new Position(reference.end.line - 1, reference.end.offset - 1)))); + + codeLens.command = { + title: referenceCount + ' ' + (referenceCount === 1 ? localize('oneReferenceLabel', 'reference') : localize('manyReferenceLabel', 'references')), + command: 'editor.action.showReferences', + arguments: [codeLens.document, codeLens.range.start, locations] + }; + return Promise.resolve(codeLens); + } + return Promise.reject(codeLens); + }).catch(() => { + codeLens.command = { + title: localize('referenceErrorLabel', 'Could not determine references'), + command: '' + }; + return Promise.resolve(codeLens); + }); + } + + private extractReferenceableSymbols(document: TextDocument, item: Proto.NavigationTree, results: Range[]) { + if (!item) { + return; + } + + const span = item.spans && item.spans[0]; + if (span) { + const range = new Range( + new Position(span.start.line - 1, span.start.offset - 1), + new Position(span.end.line - 1, span.end.offset - 1)); + + // TODO: TS currently requires the position for 'references 'to be inside of the identifer + // Massage the range to make sure this is the case + const text = document.getText(range); + + switch (item.kind) { + case PConst.Kind.const: + case PConst.Kind.let: + case PConst.Kind.variable: + case PConst.Kind.function: + // Only show references for exported variables + if (!item.kindModifiers.match(/\bexport\b/)) { + break; + } + // fallthrough + + case PConst.Kind.memberFunction: + case PConst.Kind.memberVariable: + case PConst.Kind.memberGetAccessor: + case PConst.Kind.memberSetAccessor: + case PConst.Kind.constructorImplementation: + case PConst.Kind.class: + case PConst.Kind.interface: + case PConst.Kind.type: + case PConst.Kind.enum: + const identifierMatch = new RegExp(`^(.*?(\\b|\\W))${item.text}`, 'g'); + const match = identifierMatch.exec(text); + const start = match ? match.index + match[1].length : 0; + results.push(new Range( + new Position(range.start.line, range.start.character + start), + new Position(range.start.line, range.start.character + start + item.text.length))); + break; + } + } + + (item.childItems || []).forEach(item => this.extractReferenceableSymbols(document, item, results)); + } +}; \ No newline at end of file diff --git a/extensions/typescript/src/typescriptMain.ts b/extensions/typescript/src/typescriptMain.ts index 16080631a3d..02891af3f94 100644 --- a/extensions/typescript/src/typescriptMain.ts +++ b/extensions/typescript/src/typescriptMain.ts @@ -35,6 +35,7 @@ import BufferSyncSupport from './features/bufferSyncSupport'; import CompletionItemProvider from './features/completionItemProvider'; import WorkspaceSymbolProvider from './features/workspaceSymbolProvider'; import CodeActionProvider from './features/codeActionProvider'; +import ReferenceCodeLensProvider from './features/referencesCodeLensProvider'; import * as BuildStatus from './utils/buildStatus'; import * as ProjectStatus from './utils/projectStatus'; @@ -107,6 +108,7 @@ class LanguageProvider { private formattingProvider: FormattingProvider; private formattingProviderRegistration: Disposable | null; private typingsStatus: TypingsStatus; + private referenceCodeLensProvider: ReferenceCodeLensProvider; private _validate: boolean; @@ -156,6 +158,12 @@ class LanguageProvider { this.formattingProviderRegistration = languages.registerDocumentRangeFormattingEditProvider(this.description.modeIds, this.formattingProvider); } + this.referenceCodeLensProvider = new ReferenceCodeLensProvider(client); + this.referenceCodeLensProvider.updateConfiguration(config); + if (client.apiVersion.has206Features()) { + languages.registerCodeLensProvider(this.description.modeIds, this.referenceCodeLensProvider); + } + this.description.modeIds.forEach(modeId => { let selector: DocumentFilter = { scheme: 'file', language: modeId }; languages.registerCompletionItemProvider(selector, this.completionItemProvider, '.'); @@ -171,6 +179,7 @@ class LanguageProvider { if (client.apiVersion.has213Features()) { languages.registerCodeActionsProvider(selector, new CodeActionProvider(client, modeId)); } + languages.setLanguageConfiguration(modeId, { indentationRules: { // ^(.*\*/)?\s*\}.*$ @@ -217,6 +226,9 @@ class LanguageProvider { if (this.completionItemProvider) { this.completionItemProvider.updateConfiguration(config); } + if (this.referenceCodeLensProvider) { + this.referenceCodeLensProvider.updateConfiguration(config); + } if (this.formattingProvider) { this.formattingProvider.updateConfiguration(config); if (!this.formattingProvider.isEnabled() && this.formattingProviderRegistration) {