Skip to content

Commit e46081c

Browse files
author
Oleksandr Dzhychko
authored
Merge pull request #833 from drik98/feature/add-convenience-remove-method
feat(ts-model-api): add method for removing node
2 parents 941ab4c + c5bb1f6 commit e46081c

File tree

11 files changed

+132
-32
lines changed

11 files changed

+132
-32
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { useFakeNode } from "./test-helpers";
2+
3+
test("typed nodes can be removed", () => {
4+
const { typedNode, rootNode } = useFakeNode();
5+
expect(rootNode.getChildren("children1")).toHaveLength(1);
6+
7+
typedNode.remove();
8+
expect(rootNode.getChildren("children1")).toHaveLength(0);
9+
});
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { org } from "@modelix/model-client";
2+
import { LanguageRegistry } from "@modelix/ts-model-api";
3+
import { registerLanguages } from "../build/typescript_src";
4+
5+
const DEFAULT_NODE_DATA = {
6+
root: {
7+
children: [
8+
{
9+
// concecpt ID of an PropertyAttribute
10+
concept: "mps:ceab5195-25ea-4f22-9b92-103b95ca8c0c/3364660638048049750",
11+
role: "children1",
12+
properties: {
13+
name: "aName",
14+
},
15+
},
16+
],
17+
},
18+
};
19+
20+
export function useFakeRootNode(nodeData: object = DEFAULT_NODE_DATA) {
21+
registerLanguages();
22+
23+
const { loadModelsFromJson } = org.modelix.model.client2;
24+
const rootNode = loadModelsFromJson(
25+
[JSON.stringify(nodeData)],
26+
// for the purpose of the test a change handler is not needed
27+
() => {}
28+
);
29+
30+
function getUntypedNode(role: string = "children1") {
31+
return rootNode.getChildren(role)[0];
32+
}
33+
34+
function getTypedNode(role?: string) {
35+
return LanguageRegistry.INSTANCE.wrapNode(getUntypedNode(role));
36+
}
37+
38+
return {
39+
rootNode,
40+
getUntypedNode,
41+
getTypedNode,
42+
};
43+
}
44+
45+
export function useFakeNode(role?: string, nodeData?: object) {
46+
const { getUntypedNode, getTypedNode, rootNode } = useFakeRootNode(nodeData);
47+
return {
48+
rootNode,
49+
untypedNode: getUntypedNode(role),
50+
typedNode: getTypedNode(role),
51+
};
52+
}
Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,11 @@
1-
import { org } from "@modelix/model-client";
2-
import { LanguageRegistry } from "@modelix/ts-model-api";
31
import {
42
isOfConcept_INamedConcept,
53
isOfConcept_PropertyAttribute,
64
isOfConcept_Attribute,
75
} from "../build/typescript_src/L_jetbrains_mps_lang_core";
8-
import { registerLanguages } from "../build/typescript_src";
6+
import { useFakeNode } from "./test-helpers";
97

10-
registerLanguages();
11-
12-
const nodeData = {
13-
root: {
14-
children: [
15-
{
16-
// concecpt ID of an PropertyAttribute
17-
concept: "mps:ceab5195-25ea-4f22-9b92-103b95ca8c0c/3364660638048049750",
18-
role: "children1",
19-
properties: {
20-
name: "aName",
21-
},
22-
},
23-
],
24-
},
25-
};
26-
27-
const untypedNode = useRootNode(nodeData).getChildren("children1")[0];
28-
const typedNode = LanguageRegistry.INSTANCE.wrapNode(untypedNode);
8+
const { typedNode } = useFakeNode();
299

3010
test("verifies the concept of a node", () => {
3111
expect(isOfConcept_PropertyAttribute(typedNode)).toBeTruthy();
@@ -43,12 +23,3 @@ test("nullish values are never of the type of the checked concept", () => {
4323
expect(isOfConcept_INamedConcept(null)).toBeFalsy();
4424
expect(isOfConcept_INamedConcept(undefined)).toBeFalsy();
4525
});
46-
47-
function useRootNode(nodeData: object) {
48-
const { loadModelsFromJson } = org.modelix.model.client2;
49-
return loadModelsFromJson(
50-
[JSON.stringify(nodeData)],
51-
// for the purpose of the test a change handler is not needed
52-
() => {}
53-
);
54-
}

model-api/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ kotlin {
5353
}
5454
val jsTest by getting {
5555
dependencies {
56+
implementation(project(":model-client"))
5657
}
5758
}
5859
}

model-api/src/jsMain/kotlin/org/modelix/model/api/NodeAdapterJS.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ class NodeAdapterJS(val node: INode) : INodeJS_ {
9393
node.removeChild((child as NodeAdapterJS).node)
9494
}
9595

96+
override fun remove() {
97+
node.remove()
98+
}
99+
96100
override fun getReferenceRoles(): Array<String> {
97101
return node.getReferenceRoles().toTypedArray()
98102
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) 2023-2024.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.modelix.model.api
18+
19+
import org.modelix.model.ModelFacade
20+
import kotlin.test.Test
21+
import kotlin.test.assertEquals
22+
23+
class NodeAdapterJSTest {
24+
@Test
25+
fun nodesCanBeRemoved() {
26+
val branch = ModelFacade.toLocalBranch(ModelFacade.newLocalTree())
27+
val node = branch.computeWrite {
28+
branch.getRootNode().addNewChild("roleOfTheChildThatGetsRemoved")
29+
}
30+
val jsNode = NodeAdapterJS(node)
31+
32+
jsNode.remove()
33+
34+
branch.computeRead {
35+
assertEquals(0, branch.getRootNode().allChildren.count())
36+
}
37+
}
38+
}

ts-model-api/src/ChildrenAccessor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export class SingleChildAccessor<ChildT extends ITypedNode> extends ChildrenAcce
4747
public setNew(): ChildT {
4848
const existing = this.get();
4949
if (existing !== undefined) {
50-
this.parentNode.removeChild(existing.unwrap())
50+
existing.remove();
5151
}
5252
return this.wrapChild(this.parentNode.addNewChild(this.role, 0, undefined))
5353
}

ts-model-api/src/INodeJS.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export interface INodeJS {
88
getRoleInParent(): string | undefined
99
getParent(): INodeJS | undefined
1010

11+
remove(): void
12+
1113
getChildren(role: string | undefined): Array<INodeJS>
1214
getAllChildren(): Array<INodeJS>
1315
moveChild(role: string | undefined, index: number, child: INodeJS): void

ts-model-api/src/TypedNode.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@ export class TypedNode implements ITypedNode {
88
return this._node;
99
}
1010

11+
remove(): void {
12+
this._node.remove();
13+
}
1114
}
1215

1316
export interface ITypedNode {
1417
unwrap(): INodeJS
18+
remove(): void
1519
}
1620

1721
export class UnknownTypedNode extends TypedNode {

vue-model-api/src/internal/ReactiveINodeJS.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,18 @@ test("change to all children is reactive", () => {
144144
"child3",
145145
]);
146146
});
147+
148+
test("removing a node is reactive", () => {
149+
const rootNode = useRootNode();
150+
const childCount = rootNode.getChildren("children1").length;
151+
const node = rootNode.getChildren("children1")[0];
152+
153+
// We use `computed` to test the reactivity with Vue.
154+
// Accessing the property directly would circumvent Vue
155+
// and make this test useless.
156+
const computedProperty = computed(() => rootNode.getChildren("children1"));
157+
expect(computedProperty.value).toHaveLength(childCount);
158+
159+
node.remove();
160+
expect(computedProperty.value).toHaveLength(childCount - 1);
161+
});

0 commit comments

Comments
 (0)