diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index b7ecb1411b0..71ac2878d4e 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/editorstatus'; import * as nls from 'vs/nls'; import { runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; -import { format } from 'vs/base/common/strings'; +import { format, compare } from 'vs/base/common/strings'; import { extname, basename, isEqual } from 'vs/base/common/resources'; import { areFunctions, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; @@ -50,6 +50,8 @@ import { Event } from 'vs/base/common/event'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment, IStatusbarEntry } from 'vs/workbench/services/statusbar/common/statusbar'; +import { IMarker, IMarkerService, MarkerSeverity, IMarkerData } from 'vs/platform/markers/common/markers'; +import { find } from 'vs/base/common/arrays'; class SideBySideEditorEncodingSupport implements IEncodingSupport { constructor(private master: IEncodingSupport, private details: IEncodingSupport) { } @@ -282,6 +284,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { private readonly eolElement = this._register(new MutableDisposable()); private readonly modeElement = this._register(new MutableDisposable()); private readonly metadataElement = this._register(new MutableDisposable()); + private readonly currentProblemStatus: ShowCurrentMarkerInStatusbarContribution = this._register(this.instantiationService.createInstance(ShowCurrentMarkerInStatusbarContribution)); private readonly state = new State(); private readonly activeEditorListeners = this._register(new DisposableStore()); @@ -299,7 +302,8 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { @IConfigurationService private readonly configurationService: IConfigurationService, @INotificationService private readonly notificationService: INotificationService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService, - @IStatusbarService private readonly statusbarService: IStatusbarService + @IStatusbarService private readonly statusbarService: IStatusbarService, + @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(); @@ -577,6 +581,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { this.onEncodingChange(activeControl, activeCodeEditor); this.onIndentationChange(activeCodeEditor); this.onMetadataChange(activeControl); + this.currentProblemStatus.update(activeCodeEditor); // Dispose old active editor listeners this.activeEditorListeners.clear(); @@ -594,6 +599,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { // Hook Listener for Selection changes this.activeEditorListeners.add(activeCodeEditor.onDidChangeCursorPosition((event: ICursorPositionChangedEvent) => { this.onSelectionChange(activeCodeEditor); + this.currentProblemStatus.update(activeCodeEditor); })); // Hook Listener for mode changes @@ -604,6 +610,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { // Hook Listener for content changes this.activeEditorListeners.add(activeCodeEditor.onDidChangeModelContent((e) => { this.onEOLChange(activeCodeEditor); + this.currentProblemStatus.update(activeCodeEditor); const selections = activeCodeEditor.getSelections(); if (selections) { @@ -824,6 +831,130 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { } } +class ShowCurrentMarkerInStatusbarContribution extends Disposable { + + private readonly statusBarEntryAccessor: MutableDisposable; + private editor: ICodeEditor | undefined = undefined; + private markers: IMarker[] = []; + private currentMarker: IMarker | null = null; + + constructor( + @IStatusbarService private readonly statusbarService: IStatusbarService, + @IMarkerService private readonly markerService: IMarkerService, + @IConfigurationService private readonly configurationService: IConfigurationService, + ) { + super(); + this.statusBarEntryAccessor = this._register(new MutableDisposable()); + this._register(markerService.onMarkerChanged(changedResources => this.onMarkerChanged(changedResources))); + this._register(Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('problems.showCurrentInStatus'))(() => this.updateStatus())); + } + + update(editor: ICodeEditor | undefined): void { + this.editor = editor; + this.updateStatus(); + } + + private updateStatus(): void { + const previousMarker = this.currentMarker; + this.currentMarker = this.getMarker(); + if (this.hasToUpdateStatus(previousMarker, this.currentMarker)) { + if (this.currentMarker) { + const line = this.currentMarker.message.split(/\r\n|\r|\n/g)[0]; + const text = `${this.getType(this.currentMarker)} ${line}`; + if (!this.statusBarEntryAccessor.value) { + this.statusBarEntryAccessor.value = this.statusbarService.addEntry({ text: '' }, 'statusbar.currentProblem', nls.localize('currentProblem', "Current Problem"), StatusbarAlignment.LEFT); + } + this.statusBarEntryAccessor.value.update({ text }); + } else { + this.statusBarEntryAccessor.clear(); + } + } + } + + private hasToUpdateStatus(previousMarker: IMarker | null, currentMarker: IMarker | null): boolean { + if (!currentMarker) { + return true; + } + if (!previousMarker) { + return true; + } + return IMarkerData.makeKey(previousMarker) !== IMarkerData.makeKey(currentMarker); + } + + private getType(marker: IMarker): string { + switch (marker.severity) { + case MarkerSeverity.Error: return '$(error)'; + case MarkerSeverity.Warning: return '$(warning)'; + case MarkerSeverity.Info: return '$(info)'; + } + return ''; + } + + private getMarker(): IMarker | null { + if (!this.configurationService.getValue('problems.showCurrentInStatus')) { + return null; + } + if (!this.editor) { + return null; + } + const model = this.editor.getModel(); + if (!model) { + return null; + } + const position = this.editor.getPosition(); + if (!position) { + return null; + } + return find(this.markers, marker => Range.containsPosition(marker, position)) || null; + } + + private onMarkerChanged(changedResources: ReadonlyArray): void { + if (!this.editor) { + return; + } + const model = this.editor.getModel(); + if (!model) { + return; + } + if (model && !changedResources.some(r => isEqual(model.uri, r))) { + return; + } + this.updateMarkers(); + } + + private updateMarkers(): void { + if (!this.editor) { + return; + } + const model = this.editor.getModel(); + if (!model) { + return; + } + if (model) { + this.markers = this.markerService.read({ + resource: model.uri, + severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info + }); + this.markers.sort(compareMarker); + } else { + this.markers = []; + } + this.updateStatus(); + } +} + +function compareMarker(a: IMarker, b: IMarker): number { + let res = compare(a.resource.toString(), b.resource.toString()); + if (res === 0) { + res = MarkerSeverity.compare(a.severity, b.severity); + } + if (res === 0) { + res = Range.compareRangesUsingStarts(a, b); + } + return res; +} + + function isWritableCodeEditor(codeEditor: ICodeEditor | undefined): boolean { if (!codeEditor) { return false; diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index 48671e13b75..57864213427 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -25,21 +25,10 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ActivePanelContext } from 'vs/workbench/common/panel'; -import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment, IStatusbarEntry } from 'vs/workbench/services/statusbar/common/statusbar'; -import { IMarkerService, MarkerStatistics, IMarker, MarkerSeverity, IMarkerData } from 'vs/platform/markers/common/markers'; +import { IMarkerService, MarkerStatistics } from 'vs/platform/markers/common/markers'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Event } from 'vs/base/common/event'; -import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { URI } from 'vs/base/common/uri'; -import { isEqual } from 'vs/base/common/resources'; -import { find } from 'vs/base/common/arrays'; -import { Range } from 'vs/editor/common/core/range'; -import { compare } from 'vs/base/common/strings'; registerSingleton(IMarkersWorkbenchService, MarkersWorkbenchService, false); @@ -359,136 +348,3 @@ class MarkersStatusBarContributions extends Disposable implements IWorkbenchCont } workbenchRegistry.registerWorkbenchContribution(MarkersStatusBarContributions, LifecyclePhase.Restored); - -class ShowCurrentMarkerInStatusbarContribution extends Disposable implements IEditorContribution { - - public static readonly ID = 'editor.contrib.showCurrentMarkerInStatusbar'; - - private readonly rendererDisposable: MutableDisposable; - - constructor( - private readonly editor: ICodeEditor, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IInstantiationService private readonly instantiationService: IInstantiationService - ) { - super(); - this.rendererDisposable = new MutableDisposable(); - this.onDidConfigurationChange(); - this._register(Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('problems.showCurrentInStatus'))(() => this.onDidConfigurationChange())); - } - - private onDidConfigurationChange(): void { - this.rendererDisposable.clear(); - if (this.configurationService.getValue('problems.showCurrentInStatus')) { - this.rendererDisposable.value = this.instantiationService.createInstance(ShowCurrentMarkerInStatusbarRenderer, this.editor); - } - } -} - -class ShowCurrentMarkerInStatusbarRenderer extends Disposable { - - private readonly statusBarEntryAccessor: MutableDisposable; - private markers: IMarker[] = []; - private currentMarker: IMarker | null = null; - - constructor( - private readonly editor: ICodeEditor, - @IStatusbarService private readonly statusbarService: IStatusbarService, - @IMarkerService private readonly markerService: IMarkerService - ) { - super(); - this.statusBarEntryAccessor = this._register(new MutableDisposable()); - this._register(markerService.onMarkerChanged(changedResources => this.onMarkerChanged(changedResources))); - this._register(editor.onDidChangeModel(() => this.updateMarkers())); - this._register(editor.onDidChangeCursorPosition(() => this.render())); - this.render(); - } - - private render(): void { - const previousMarker = this.currentMarker; - this.currentMarker = this.getMarker(); - if (this.hasToUpdateStatus(previousMarker, this.currentMarker)) { - this.updateStatus(); - } - } - - private hasToUpdateStatus(previousMarker: IMarker | null, currentMarker: IMarker | null): boolean { - if (!currentMarker) { - return true; - } - if (!previousMarker) { - return true; - } - return IMarkerData.makeKey(previousMarker) !== IMarkerData.makeKey(currentMarker); - } - - private updateStatus(): void { - if (this.currentMarker) { - const line = this.currentMarker.message.split(/\r\n|\r|\n/g)[0]; - const text = `${this.getType(this.currentMarker)} ${line}`; - if (this.statusBarEntryAccessor.value) { - this.statusBarEntryAccessor.value.update({ text }); - } else { - this.statusBarEntryAccessor.value = this.statusbarService.addEntry({ text }, 'statusbar.currentProblem', localize('currentProblem', "Current Problem"), StatusbarAlignment.LEFT); - } - } else { - this.statusBarEntryAccessor.clear(); - } - } - - private getType(marker: IMarker): string { - switch (marker.severity) { - case MarkerSeverity.Error: return '$(error)'; - case MarkerSeverity.Warning: return '$(warning)'; - case MarkerSeverity.Info: return '$(info)'; - } - return ''; - } - - private getMarker(): IMarker | null { - const model = this.editor.getModel(); - if (!model) { - return null; - } - const position = this.editor.getPosition(); - if (!position) { - return null; - } - return find(this.markers, marker => Range.containsPosition(marker, position)) || null; - } - - private onMarkerChanged(changedResources: ReadonlyArray): void { - const editorModel = this.editor.getModel(); - if (editorModel && !changedResources.some(r => isEqual(editorModel.uri, r))) { - return; - } - this.updateMarkers(); - } - - private updateMarkers(): void { - const editorModel = this.editor.getModel(); - if (editorModel) { - this.markers = this.markerService.read({ - resource: editorModel.uri, - severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info - }); - this.markers.sort(compareMarker); - } else { - this.markers = []; - } - this.render(); - } -} - -function compareMarker(a: IMarker, b: IMarker): number { - let res = compare(a.resource.toString(), b.resource.toString()); - if (res === 0) { - res = MarkerSeverity.compare(a.severity, b.severity); - } - if (res === 0) { - res = Range.compareRangesUsingStarts(a, b); - } - return res; -} - -registerEditorContribution(ShowCurrentMarkerInStatusbarContribution.ID, ShowCurrentMarkerInStatusbarContribution);