Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup markers on close & consider marker reason #153

Merged
merged 1 commit into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- [backend] Added support for using custom JVM args in `GLSPSocketServerContribution` [#125](https://github.com/eclipse-glsp/glsp-theia-integration/pull/125)
- [diagram] Fixed a bug that prevented proper focus tracking when switching between tabs [#132](https://github.com/eclipse-glsp/glsp-theia-integration/pull/132)
- [diagram] Fixed a bug that could cause dispatching of `SaveActions` even if the diagram is not dirty [#141](https://github.com/eclipse-glsp/glsp-theia-integration/pull/141) - Contributed on behalf of STMicroelectronics
- [validation] Only keep live validation markers in problems view and clean all others [153](https://github.com/eclipse-glsp/glsp-theia-integration/pull/153)

### Breaking Changes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { bindOrRebind, ExternalMarkerManager, IActionDispatcher, Marker, MarkerKind, TYPES } from '@eclipse-glsp/client/lib';
import { bindOrRebind, ExternalMarkerManager, IActionDispatcher, Marker, MarkerKind, MarkersReason, TYPES } from '@eclipse-glsp/client/lib';
import URI from '@theia/core/lib/common/uri';
import { Container, inject, injectable, optional, postConstruct } from '@theia/core/shared/inversify';
import { ProblemManager } from '@theia/markers/lib/browser/problem/problem-manager';
import { Diagnostic } from 'vscode-languageserver-types';

import { ApplicationShell, Widget } from '@theia/core/lib/browser';
import { SelectionWithElementIds } from '../theia-opener-options-navigation-service';
import { getDiagramWidget } from './glsp-diagram-widget';

export const TheiaMarkerManagerFactory = Symbol('TheiaMarkerManagerFactory');

Expand All @@ -36,20 +38,43 @@ export function connectTheiaMarkerManager(
}
}

type ValidationMarker = Marker & { reason?: string };

class DiagnosticMarkers {
protected diagnostic2marker = new Map<Diagnostic, Marker>();
protected diagnostic2marker = new Map<Diagnostic, ValidationMarker>();
get size(): number {
return this.diagnostic2marker.size;
}
all(): IterableIterator<Marker> {
all(): IterableIterator<ValidationMarker> {
return this.diagnostic2marker.values();
}
marker(diagnostic: Diagnostic): Marker | undefined {
marker(diagnostic: Diagnostic): ValidationMarker | undefined {
return this.diagnostic2marker.get(diagnostic);
}
add(diagnostic: Diagnostic, marker: Marker): Map<Diagnostic, Marker> {
add(diagnostic: Diagnostic, marker: ValidationMarker): Map<Diagnostic, ValidationMarker> {
return this.diagnostic2marker.set(diagnostic, marker);
}
getMarkerByOrigin(origin?: string): ValidationMarker[] {
return Array.from(this.diagnostic2marker.values()).filter(marker => marker.reason === origin);
}
getByOrigin(origin?: string): Diagnostic[] {
const diagnostics: Diagnostic[] = [];
this.diagnostic2marker.forEach((marker, diagnostic) => {
if (marker.reason === origin) {
diagnostics.push(diagnostic);
}
});
return diagnostics;
}
deleteByOrigin(origin?: string): void {
const toDelete: Diagnostic[] = [];
this.diagnostic2marker.forEach((marker, diagnostic) => {
if (marker.reason === origin) {
toDelete.push(diagnostic);
}
});
toDelete.forEach(diagnostic => this.delete(diagnostic));
}
delete(diagnostic: Diagnostic): boolean {
return this.diagnostic2marker.delete(diagnostic);
}
Expand All @@ -60,25 +85,31 @@ class DiagnosticMarkers {

@injectable()
export class TheiaMarkerManager extends ExternalMarkerManager {
protected markerReasonsToKeep = [MarkersReason.LIVE];

@inject(ProblemManager) @optional() protected readonly problemManager?: ProblemManager;
@inject(ApplicationShell) @optional() protected readonly shell?: ApplicationShell;

protected uri2markers = new Map<URI, DiagnosticMarkers>();
protected uri2markers = new Map<string, DiagnosticMarkers>();

protected markers(uri: URI): DiagnosticMarkers {
const marker = this.uri2markers.get(uri);
if (marker === undefined) {
const markers = this.uri2markers.get(uri.toString());
if (markers === undefined) {
const newMarker = new DiagnosticMarkers();
this.uri2markers.set(uri, newMarker);
this.uri2markers.set(uri.toString(), newMarker);
return newMarker;
}
return marker;
return markers;
}

@postConstruct()
protected initialize(): void {
if (this.problemManager) {
this.problemManager.onDidChangeMarkers(uri => this.refreshMarker(uri));
}
if (this.shell) {
this.shell.onDidRemoveWidget(widget => this.handleWidgetClose(widget));
}
}

protected async refreshMarker(uri: URI): Promise<void> {
Expand All @@ -103,23 +134,25 @@ export class TheiaMarkerManager extends ExternalMarkerManager {
}
}

setMarkers(markers: Marker[], sourceUri?: string): void {
setMarkers(markers: Marker[], reason?: string, sourceUri?: string): void {
if (this.problemManager === undefined) {
return;
}
const uri = new URI(sourceUri);

this.markers(uri).deleteByOrigin(reason);
const existingOtherMarkers = [...this.markers(uri).all()];
this.markers(uri).clear();
this.problemManager.setMarkers(
uri,
this.languageLabel,
markers.map(marker => this.createDiagnostic(uri, marker))
);

const existingOtherDiagnostics = existingOtherMarkers.map(marker => this.createDiagnostic(uri, marker, marker.reason));
const newDiagnostics = markers.map(marker => this.createDiagnostic(uri, marker, reason));
this.problemManager.setMarkers(uri, this.languageLabel, [...existingOtherDiagnostics, ...newDiagnostics]);
}

protected createDiagnostic(uri: URI, marker: Marker): Diagnostic {
protected createDiagnostic(uri: URI, marker: Marker, origin?: string): Diagnostic {
const range = SelectionWithElementIds.createRange([marker.elementId]);
const diagnostic = Diagnostic.create(range, marker.label, this.toSeverity(marker.kind));
this.markers(uri).add(diagnostic, marker);
this.markers(uri).add(diagnostic, { ...marker, reason: origin });
return diagnostic;
}

Expand All @@ -135,4 +168,22 @@ export class TheiaMarkerManager extends ExternalMarkerManager {
return undefined;
}
}

protected handleWidgetClose(widget: Widget): void {
const resourceUri = getDiagramWidget(widget)?.getResourceUri();
if (resourceUri) {
this.clearMarkers(resourceUri, this.markerReasonsToKeep);
}
}

protected clearMarkers(uri: URI, exceptThoseWithReasons: string[]): void {
const diagnostics = [];
for (const reason of exceptThoseWithReasons) {
const markersToKeep = this.markers(uri).getMarkerByOrigin(reason);
this.markers(uri).clear();
const diagnosticsToKeep = markersToKeep.map(marker => this.createDiagnostic(uri, marker, reason));
diagnostics.push(...diagnosticsToKeep);
}
this.problemManager?.setMarkers(uri, this.languageLabel, diagnostics);
}
}
Loading