Skip to content

Commit f402a01

Browse files
committed
highlight ports with invalid behaviour
1 parent b060477 commit f402a01

File tree

6 files changed

+57
-37
lines changed

6 files changed

+57
-37
lines changed

src/features/dfdElements/AssignmentLanguage.ts

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
Token,
88
WordCompletion,
99
} from "../constraintMenu/AutoCompletion";
10-
import { SModelElementImpl, SModelRootImpl, SParentElementImpl, SPortImpl } from "sprotty";
10+
import { SModelElementImpl, SParentElementImpl, SPortImpl } from "sprotty";
1111
import { LabelTypeRegistry } from "../labels/labelTypeRegistry";
1212
import { DfdNodeImpl } from "./nodes";
1313

@@ -148,14 +148,14 @@ export class ReplaceAutoCompleteTree extends AutoCompleteTree {
148148

149149
export namespace TreeBuilder {
150150
export function buildTree(
151-
model: SModelRootImpl,
152151
labelTypeRegistry: LabelTypeRegistry,
152+
port?: SPortImpl,
153153
): AutoCompleteNode<WordOrReplacableWord>[] {
154154
return [
155155
buildSetOrUnsetStatement(labelTypeRegistry, "set"),
156156
buildSetOrUnsetStatement(labelTypeRegistry, "unset"),
157-
buildForwardStatement(model),
158-
buildAssignStatement(labelTypeRegistry, model),
157+
buildForwardStatement(port),
158+
buildAssignStatement(labelTypeRegistry, port),
159159
];
160160
}
161161

@@ -173,9 +173,9 @@ export namespace TreeBuilder {
173173
};
174174
}
175175

176-
function buildForwardStatement(model: SModelRootImpl) {
176+
function buildForwardStatement(port?: SPortImpl) {
177177
const inputNode: AutoCompleteNode = {
178-
word: new InputListWord(model),
178+
word: new InputListWord(port),
179179
children: [],
180180
};
181181
return {
@@ -186,20 +186,20 @@ export namespace TreeBuilder {
186186

187187
function buildAssignStatement(
188188
labelTypeRegistry: LabelTypeRegistry,
189-
model: SModelRootImpl,
189+
port?: SPortImpl,
190190
): AutoCompleteNode<WordOrReplacableWord> {
191191
const fromNode: AutoCompleteNode = {
192192
word: new ConstantWord("from"),
193193
children: [
194194
{
195-
word: new InputListWord(model),
195+
word: new InputListWord(port),
196196
children: [],
197197
},
198198
],
199199
};
200200
const ifNode: AutoCompleteNode = {
201201
word: new ConstantWord("if"),
202-
children: buildCondition(model, labelTypeRegistry, fromNode),
202+
children: buildCondition(labelTypeRegistry, fromNode, port),
203203
};
204204
return {
205205
word: new ConstantWord("assign"),
@@ -212,7 +212,7 @@ export namespace TreeBuilder {
212212
};
213213
}
214214

215-
function buildCondition(model: SModelRootImpl, labelTypeRegistry: LabelTypeRegistry, nextNode: AutoCompleteNode) {
215+
function buildCondition(labelTypeRegistry: LabelTypeRegistry, nextNode: AutoCompleteNode, port?: SPortImpl) {
216216
const connectors: AutoCompleteNode[] = ["&&", "||"].map((o) => ({
217217
word: new ConstantWord(o),
218218
children: [],
@@ -221,7 +221,7 @@ export namespace TreeBuilder {
221221
const expressors: AutoCompleteNode[] = [
222222
new ConstantWord("TRUE"),
223223
new ConstantWord("FALSE"),
224-
new InputLabelWord(model, labelTypeRegistry),
224+
new InputLabelWord(labelTypeRegistry, port),
225225
].map((e) => ({
226226
word: e,
227227
children: [...connectors, nextNode],
@@ -236,20 +236,14 @@ export namespace TreeBuilder {
236236
}
237237

238238
abstract class InputAwareWord {
239-
constructor(private model: SModelRootImpl) {}
239+
constructor(private port?: SPortImpl) {}
240240

241241
protected getAvailableInputs(): string[] {
242-
const selectedPorts = this.getSelectedPorts(this.model);
243-
if (selectedPorts.length === 0) {
244-
return [];
245-
}
246-
return selectedPorts.flatMap((port) => {
247-
const parent = port.parent;
248-
if (!(parent instanceof DfdNodeImpl)) {
249-
return [];
250-
}
242+
const parent = this.port?.parent;
243+
if (parent && parent instanceof DfdNodeImpl) {
251244
return parent.getAvailableInputs().filter((input) => input !== undefined) as string[];
252-
});
245+
}
246+
return [];
253247
}
254248

255249
private getSelectedPorts(node: SModelElementImpl): SPortImpl[] {
@@ -386,8 +380,8 @@ class InputWord extends InputAwareWord implements ReplaceableAbstractWord {
386380

387381
class InputListWord implements ReplaceableAbstractWord {
388382
private inputWord: InputWord;
389-
constructor(model: SModelRootImpl) {
390-
this.inputWord = new InputWord(model);
383+
constructor(port?: SPortImpl) {
384+
this.inputWord = new InputWord(port);
391385
}
392386

393387
completionOptions(word: string): WordCompletion[] {
@@ -426,8 +420,8 @@ class InputLabelWord implements ReplaceableAbstractWord {
426420
private inputWord: InputWord;
427421
private labelWord: LabelWord;
428422

429-
constructor(model: SModelRootImpl, labelTypeRegistry: LabelTypeRegistry) {
430-
this.inputWord = new InputWord(model);
423+
constructor(labelTypeRegistry: LabelTypeRegistry, port?: SPortImpl) {
424+
this.inputWord = new InputWord(port);
431425
this.labelWord = new LabelWord(labelTypeRegistry);
432426
}
433427

src/features/dfdElements/behaviorRefactorer.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
SEdgeImpl,
1111
SLabelImpl,
1212
SModelElementImpl,
13-
SModelRootImpl,
1413
SParentElementImpl,
1514
TYPES,
1615
} from "sprotty";
@@ -87,8 +86,8 @@ export class DFDBehaviorRefactorer {
8786
this.logger.log(this, "Changed labels", changedLabels);
8887

8988
const model = await this.commandStack.executeAll([]);
90-
const tree = new ReplaceAutoCompleteTree(TreeBuilder.buildTree(model, this.registry));
9189
this.traverseDfdOutputPorts(model, (port) => {
90+
const tree = new ReplaceAutoCompleteTree(TreeBuilder.buildTree(this.registry, port));
9291
this.renameLabelsForPort(port, changedLabels, tree);
9392
});
9493

@@ -118,7 +117,6 @@ export class DFDBehaviorRefactorer {
118117
port: DfdInputPortImpl,
119118
oldLabelText: string,
120119
newLabelText: string,
121-
root: SModelRootImpl,
122120
): Map<string, string> {
123121
label.text = oldLabelText;
124122
const oldInputName = port.getName();
@@ -131,7 +129,7 @@ export class DFDBehaviorRefactorer {
131129
return behaviorChanges;
132130
}
133131

134-
const tree = new ReplaceAutoCompleteTree(TreeBuilder.buildTree(root, this.registry));
132+
const tree = new ReplaceAutoCompleteTree(TreeBuilder.buildTree(this.registry, port));
135133

136134
node.children.forEach((child) => {
137135
if (!(child instanceof DfdOutputPortImpl)) {
@@ -209,7 +207,6 @@ export class RefactorInputNameInDFDBehaviorCommand extends Command {
209207
port,
210208
oldInputName,
211209
newInputName,
212-
context.root,
213210
);
214211
behaviorChanges.forEach((updatedBehavior, id) => {
215212
const port = context.root.index.getById(id);

src/features/dfdElements/elementStyles.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@
7272
/* Ports */
7373

7474
.sprotty-port rect {
75-
stroke: var(--color-foreground);
76-
fill: color-mix(in srgb, var(--color-primary), var(--color-background) 25%);
75+
stroke: var(--port-border, var(--color-foreground));
76+
fill: color-mix(in srgb, var(--port-color, var(--color-primary)), var(--color-background) 25%);
7777
stroke-width: 0.5;
7878
}
7979

src/features/dfdElements/outputPortEditUi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ export class OutputPortEditUI extends AbstractUIExtension implements Switchable
318318
readOnly: this.editorModeController?.isReadOnly() ?? false,
319319
});
320320

321-
this.tree = new ReplaceAutoCompleteTree(TreeBuilder.buildTree(root, this.labelTypeRegistry));
321+
this.tree = new ReplaceAutoCompleteTree(TreeBuilder.buildTree(this.labelTypeRegistry, this.port));
322322

323323
// Validation of loaded behavior text.
324324
this.validateBehavior();

src/features/dfdElements/ports.tsx

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ import {
1313
} from "sprotty";
1414
import { Bounds, SPort } from "sprotty-protocol";
1515
import { injectable } from "inversify";
16-
import { VNode } from "snabbdom";
16+
import { VNode, VNodeStyle } from "snabbdom";
1717
import { ArrowEdgeImpl } from "./edges";
18+
import { AutoCompleteTree } from "../constraintMenu/AutoCompletion";
19+
import { TreeBuilder } from "./AssignmentLanguage";
20+
import { labelTypeRegistry } from "../..";
1821

1922
const defaultPortFeatures = [...SPortImpl.DEFAULT_FEATURES, moveFeature, deletableFeature];
2023
const portSize = 7;
@@ -94,6 +97,12 @@ export class DfdOutputPortImpl extends SPortImpl {
9497
static readonly DEFAULT_FEATURES = [...defaultPortFeatures, withEditLabelFeature];
9598

9699
behavior: string = "";
100+
private tree: AutoCompleteTree;
101+
102+
constructor() {
103+
super();
104+
this.tree = new AutoCompleteTree(TreeBuilder.buildTree(labelTypeRegistry, this));
105+
}
97106

98107
override get bounds(): Bounds {
99108
return {
@@ -117,19 +126,37 @@ export class DfdOutputPortImpl extends SPortImpl {
117126
// Only allow edges from this port outwards
118127
return role === "source";
119128
}
129+
130+
/**
131+
* Generates the per-node inline style object for the view.
132+
*/
133+
geViewStyleObject(): VNodeStyle {
134+
const style: VNodeStyle = {
135+
opacity: this.opacity.toString(),
136+
};
137+
if (!labelTypeRegistry) return style;
138+
const valid = this.tree.verify(this.behavior.split("\n")).length == 0;
139+
140+
if (!valid) {
141+
style["--port-border"] = "#ff0000";
142+
style["--port-color"] = "#ff6961";
143+
}
144+
145+
return style;
146+
}
120147
}
121148

122149
@injectable()
123150
export class DfdOutputPortView extends ShapeView {
124-
render(node: Readonly<SPortImpl>, context: RenderingContext): VNode | undefined {
151+
render(node: Readonly<DfdOutputPortImpl>, context: RenderingContext): VNode | undefined {
125152
if (!this.isVisible(node, context)) {
126153
return undefined;
127154
}
128155

129156
const { width, height } = node.bounds;
130157

131158
return (
132-
<g class-sprotty-port={true} class-selected={node.selected} style={{ opacity: node.opacity.toString() }}>
159+
<g class-sprotty-port={true} class-selected={node.selected} style={node.geViewStyleObject()}>
133160
<rect x="0" y="0" width={width} height={height} />
134161
<text x={width / 2} y={height / 2} class-port-text={true}>
135162
O

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { settingsModule } from "./features/settingsMenu/di.config";
3434
import { LoadDiagramAction } from "./features/serialize/load";
3535
import { commandPaletteModule } from "./features/commandPalette/di.config";
3636
import { LoadingIndicator } from "./common/loadingIndicator";
37+
import { LabelTypeRegistry } from "./features/labels/labelTypeRegistry";
3738

3839
const container = new Container();
3940

@@ -77,6 +78,7 @@ export function setModelFileName(name: string): void {
7778
export function getModelFileName(): string {
7879
return modelFileName;
7980
}
81+
export const labelTypeRegistry = container.get<LabelTypeRegistry>(LabelTypeRegistry);
8082

8183
export function setModelSource(file: File): void {
8284
modelSource

0 commit comments

Comments
 (0)