From 4f2133e6f3c2bad0c2c95c41170a67a46c639628 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 15 Dec 2016 18:00:09 +0100 Subject: [PATCH 01/77] #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 f151f537b63c..1598092179c2 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 2a246672bcfd..ade50f9930d6 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 c9971e6feabb..8b14f5ce0d79 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 78a0c801700a..17343c404335 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 d8218a1a5e25..17ef49ad855d 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 fb3d68dd0fc9..a06097b1ea86 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 02/77] #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 1598092179c2..47618a057d18 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 ade50f9930d6..f93e27a93663 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 8b14f5ce0d79..2f4815dc7cf7 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 17343c404335..38a2dbcecc6b 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 17ef49ad855d..5f3f12b4beff 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 03/77] 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 f93e27a93663..e654e52c526c 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 04/77] #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 e654e52c526c..aaa5c6e4a20e 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 05/77] 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 3b68c03d4c96..2fb4383d637a 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 06/77] #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 6104e528281e..b03fca8e56e4 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 aaa5c6e4a20e..e4a75b3adcf4 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 a06097b1ea86..ecbb54549bec 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 07/77] #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 e4a75b3adcf4..8d35c13e780c 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 5f3f12b4beff..2b3ed6940e55 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 ecbb54549bec..e01c1dd977f0 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 8bca6d86aaa0664b121d63fc3bc262770b39a162 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 3 Jan 2017 18:39:45 +0100 Subject: [PATCH 08/77] #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 ad0bc3ef5085..27d0ce1e4697 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 8d35c13e780c..bed793481b31 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 e01c1dd977f0..57ca716dda37 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 68274bfd8f3d724874b8aead1c0f5aa663ad156c Mon Sep 17 00:00:00 2001 From: roblou Date: Tue, 3 Jan 2017 15:57:28 -0800 Subject: [PATCH 09/77] 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 49de2d67ff0a..24e2a7b4c056 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 66fed1dbee6b..26c1bc05d25f 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 255eae24ba1d..fe752040bfeb 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 8b3a46f966d4..090bea80e1c1 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 ecbf70e253d42a3fcb1069011ab3663153a067ea Mon Sep 17 00:00:00 2001 From: roblou Date: Tue, 3 Jan 2017 17:29:23 -0800 Subject: [PATCH 10/77] 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 24e2a7b4c056..08e9598402a3 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 26c1bc05d25f..c514ad5bab91 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 fe752040bfeb..b05808819092 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 090bea80e1c1..42daf7da501f 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 11/77] 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 b59f4b267cc8..10029064360c 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 3e8879bdb90c..d7453bc6b7b2 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 b05808819092..748b1904ff29 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 98374166016bdfd3793ab5127ff4017e374a9b5c Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Wed, 4 Jan 2017 12:08:28 +0100 Subject: [PATCH 12/77] 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 e0359c5d0ebb..846b10ecdced 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 13/77] 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 7ede1f4eb2ba..ac4094b5fe15 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 bc9e500d5943..bd997951c328 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 14/77] :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 2eb260b91bb6..176cc70a5016 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 b899d0f7350b..fadef1d89ea9 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 8a442c17f1a5..000000000000 --- 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 3906356d003c..0eb5f89db377 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 d47010f4e0b3..f05d9576d9bd 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 ee18a5ec9d2d..2bd0f2a3d452 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 084eacd9782b..92de20b38efe 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 4f0fc157da35..fe3889b29184 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 f8b5625a7376..fb4c42cd4520 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 15/77] 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 cecec707c43b..62c1ccd9ec18 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 16/77] 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 21eae022f5b8..4da6a3a3b36e 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 17/77] 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 c5ceceb4cf42..2c36a2f80126 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 f05b962354a0..64ab0b7ed485 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 c5dd01e143ad..88bb1de5ce3d 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 08263b5fe238..6ee74bb6e7ab 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 18/77] 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 b2c3eac01c29..a1a79db6826a 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 19/77] 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 a1a79db6826a..bc7a744c14f8 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 20/77] :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 fc522397f667..a78e1d41fb63 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 21/77] 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 23a4ebba24f9..684445fd3744 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 22/77] 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 a78e1d41fb63..08bbaeb674b8 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 aa331b329d3a..3c70130c9910 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 23/77] 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 08bbaeb674b8..6ff69fe2e54e 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 24/77] 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 6ff69fe2e54e..d5b2bbe1697c 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 3c70130c9910..f6e14bc8b935 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 25/77] 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 d5b2bbe1697c..7e8eab227da5 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 f6e14bc8b935..7304fc44d367 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 26/77] 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 07181e043b56..2e56d1240c97 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 27/77] 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 0f9d61b48278..828364cbc408 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 28/77] 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 031589c965ac..5839acf6301a 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 29/77] "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 684445fd3744..0562753bed2a 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 30/77] #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 ce92c4c1a148..afa47d7ebac7 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 31829a119013..b27ab709b675 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 31/77] #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 afa47d7ebac7..ebf4a30612c9 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 b27ab709b675..31829a119013 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 32/77] 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 65a725816fae..1894dab23bac 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 33/77] update distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d099f65135a3..c6c21afc8cf2 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 34/77] :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 d5c0da3e5427..56536188d211 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 35/77] 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 c99ab5c20ac6..e1acf4872ec1 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 36/77] 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 94b082fb8ff3..8006157ba6cc 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 7b9579c7f957..99b26caf5ef5 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 37/77] 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 b03d213e3483..b7c60e8f6c23 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 38/77] 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 d789272604af..ea5a3ee80fe6 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 4717f2ec35c5..dc18cc01c1f1 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 8006157ba6cc..623df36d1cb9 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 39/77] 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 2f2f422e136c..997807ba6e98 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 86188d6eb8de..c665252b3e51 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 0562753bed2a..68921520b504 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 ad7f18836986..910bbd3b8bb4 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 c8e9b640f37d..576bba7a8932 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 40/77] 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 9da334462284..d622ec34313a 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 41/77] 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 c0949423672a..4aa82872e94e 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 42/77] 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 dfbd18ee3109..ba99b7663df1 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 43/77] 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 979421d8f9da..10e0304f3a2c 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 44/77] 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 80d41601b7bb..47ed1616faa8 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 026fad8e55b5..e413e7775bf6 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 b9e11a0702a5..5eb0da6e884b 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 a95ec8f2bea2..36ecf5f1fb64 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 caa91b82bb41..c6f6cc136c52 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 45/77] 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 4cab80a45a9f..469b19bd79d5 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 2ca2968c6c30..b399d1b1d81d 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 e0ff2b694124..5c5102380963 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 1894dab23bac..71cb92ffd940 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 c6c21afc8cf2..84846e8827e3 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 f8693d9cd69d..f69d864e509e 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 ce1c82562acf..24b528847521 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 bd7e75d2a243..ce4524b4ded9 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 46/77] 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 5c5102380963..51a9aed5a67c 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 47/77] 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 ad18e62f4c36..12e36d958e9f 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 48/77] 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 e74b027c1883..ceb3ae212006 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 49/77] 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 68921520b504..313bceae8bc5 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 50/77] 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 dac4d20c36e9..258c0a29a136 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 51/77] 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 313bceae8bc5..9a6b23de05bd 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 9ed159fe91be..92beb99600d6 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 3519e4256495..ecec45008b08 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 910bbd3b8bb4..75c04ff7cded 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 3dd75e0e8dbc..8c368528db6b 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 52/77] 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 5839acf6301a..238afa870ec4 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 53/77] 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 26cdc0c5193a..34a5c02a2fe0 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 54/77] 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 d7453bc6b7b2..fe1198623fe5 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 55/77] 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 a0d329f9f8f4..8acfffe05676 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 56/77] 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 5a2687d5055c..0c733246563c 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 57/77] 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 f4fb6eb0dfb3..27ffed195548 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 58/77] 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 df728f73675c..e51a24cfefb8 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 576bba7a8932..d82164b14522 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 59/77] #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 491e4ac3590a..2cdcded90d2e 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 892ba87928d0..6c31b27d5e75 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 b4f7cb01317e..11a0c7099229 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 60/77] 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 289e0e4fb154..d45cc975b8d5 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 56536188d211..dd967a6e14c6 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 10f15341b35c..3c1e03ac623a 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 b7c60e8f6c23..5d438e137a2c 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 8366aa6fe641..19d7c77d7bac 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 a0eb9caa6f18..2b92b832272e 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 61/77] 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 790de1fcc919..21c69e610901 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 62/77] 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 9f3515b0d95b..000000000000 --- 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 1b19e91e145c..000000000000 --- 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 59e007293683..000000000000 --- 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 4084eb9362d5..505fe390815f 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 3ffa3e17768b..f5d1c5c4c9cd 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 cc391d5aa7da..4f2a20f03730 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 9847b83b1f00..e2ba6cde2ed5 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 3a5417c1ff82..ca52028f2615 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 2b92b832272e..ffec104d14e6 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 52845c580bb5..1d325306e0a7 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 63/77] 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 846b10ecdced..5ecb9a1f315d 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 64/77] 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 11f9a39f1b1a..dfd9904fa86f 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 e3e072b288e2..37f5a52df217 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 d9747208de95..18a161598544 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 65/77] 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 f27caed5b945..f6c97a0c0b4d 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 66/77] 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 bc7a744c14f8..96c0a7da7ba8 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 8bbd3b3911d8..a6260610aec7 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 67/77] 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 2cda12aa0b44..64e21cd4d8d1 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 68/77] 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 f3e53ff11195..24d871f3a585 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 69/77] 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 9a6b23de05bd..5b97853ef25b 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 70/77] Added download links for stable and insiders --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 91566a493db6..5d5ee55c63af 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 71/77] 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 748b1904ff29..1fcd97fd7735 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 72/77] 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 113024a23dbd..1cca913569e0 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 73/77] 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 2e56d1240c97..329ad48c685c 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 74/77] 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 1cca913569e0..1d0411b43f08 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 329ad48c685c..b6872ffdd473 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 75/77] 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 b6872ffdd473..f2b76b3f41c2 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 76/77] 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 1d0411b43f08..ea388837ada5 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 77/77] 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 ea388837ada5..7d05f7e29aa5 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