Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { GModelRoot, GNode } from '@eclipse-glsp/graph';
import { ChangeBoundsOperation, Dimension, MaybePromise, Point } from '@eclipse-glsp/protocol';
import { GModelRoot, GNode, isGBoundsAware } from '@eclipse-glsp/graph';
import { ChangeBoundsOperation, Dimension, ElementAndBounds, MaybePromise, Point } from '@eclipse-glsp/protocol';
import { injectable } from 'inversify';
import { Command } from '../command/command';
import { GModelOperationHandler } from './gmodel-operation-handler';
Expand All @@ -27,7 +27,23 @@ export class GModelChangeBoundsOperationHandler extends GModelOperationHandler {
operationType = ChangeBoundsOperation.KIND;

createCommand(operation: ChangeBoundsOperation): MaybePromise<Command | undefined> {
return this.commandOf(() => this.executeChangeBounds(operation));
const newBounds = operation.newBounds.filter(element => this.hasChanged(element));
if (newBounds.length === 0) {
return undefined;
}
return this.commandOf(() => this.executeChangeBounds({ ...operation, newBounds }));
}

protected hasChanged(element: ElementAndBounds): boolean {
const knownElement = this.modelState.index.find(element.elementId);
if (!knownElement || !isGBoundsAware(knownElement)) {
return true;
}
const sizeChanged = knownElement.size ? !Dimension.equals(knownElement.size, element.newSize) : true;
if (sizeChanged) {
return true;
}
return knownElement.position && element.newPosition ? !Point.equals(knownElement.position, element.newPosition) : true;
}

protected executeChangeBounds(operation: ChangeBoundsOperation): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { ChangeRoutingPointsOperation, MaybePromise } from '@eclipse-glsp/protocol';
import { GEdge } from '@eclipse-glsp/graph';
import { ChangeRoutingPointsOperation, ElementAndRoutingPoints, MaybePromise, Point } from '@eclipse-glsp/protocol';
import { injectable } from 'inversify';
import { Command } from '../command/command';
import { GLSPServerError } from '../utils/glsp-server-error';
import { applyRoutingPoints } from '../utils/layout-util';
import { GModelOperationHandler } from './gmodel-operation-handler';

Expand All @@ -26,14 +26,27 @@ export class GModelChangeRoutingPointsOperationHandler extends GModelOperationHa
operationType = ChangeRoutingPointsOperation.KIND;

createCommand(operation: ChangeRoutingPointsOperation): MaybePromise<Command | undefined> {
return this.commandOf(() => this.executeChangeRoutingPoints(operation));
const newRoutingPoints = operation.newRoutingPoints.filter(newRoutingPoints => this.hasChanged(newRoutingPoints));
if (newRoutingPoints.length === 0) {
return undefined;
}
return this.commandOf(() => this.executeChangeRoutingPoints({ ...operation, newRoutingPoints }));
}

executeChangeRoutingPoints(operation: ChangeRoutingPointsOperation): MaybePromise<void> {
if (!operation.newRoutingPoints) {
throw new GLSPServerError('Incomplete change routingPoints action');
protected hasChanged(element: ElementAndRoutingPoints): boolean {
const knownElement = this.modelState.index.findByClass(element.elementId, GEdge);
if (!knownElement || knownElement.routingPoints.length !== element.newRoutingPoints?.length) {
return true;
}
for (let i = 0; i < knownElement.routingPoints.length; i++) {
if (!Point.equals(knownElement.routingPoints[i], element.newRoutingPoints[i])) {
return true;
}
}
return false;
}

executeChangeRoutingPoints(operation: ChangeRoutingPointsOperation): MaybePromise<void> {
const index = this.modelState.index;
operation.newRoutingPoints.forEach(routingPoints => applyRoutingPoints(routingPoints, index));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ export class CompoundOperationHandler extends OperationHandler {
commands.push(command);
}
}
return new CompoundCommand(commands);
return commands.length > 0 ? new CompoundCommand(commands) : undefined;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ export class OperationActionHandler implements ActionHandler {
const command = await handler.execute(operation);
if (command) {
await this.executeCommand(command);
return this.submitModel();
}
return this.modelSubmissionHandler.submitModel('operation');
return [];
Copy link
Contributor Author

@martin-fleck-at martin-fleck-at Feb 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may change the behavior in a way that the GModel is not re-generated after an empty operation. Adopters may have relied on that so this would constitute a breaking change as the previous behavior is also valid and may not strictly be classified as a bug. So I am happy to discuss whether this should be changed or not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this is more of a bugfix to ensure that the operation handler works as expected.
Nevertheless your are right in certain edge cases adopters may have relied on a model update
(I can`t think of any particular usecase where a effectivly no-op update would be required but who knows).
Maybe we should at least refactor this in overrideable method or property to configure whether update on empty commands should be enabled?
We disable it by default but atleast it gives adopters an easy entry point to change the behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree now that we can consider this more of a bugfix since the Java server was behaving like this all along. So I would almost argue that we do not need an additional flag. The method as-is is already very short and could be overwritten, don't you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼 Fine with me.

}

protected async executeCommand(command: Command): Promise<void> {
Expand Down
Loading