Skip to content

Commit

Permalink
Merge pull request #833 from drik98/feature/add-convenience-remove-me…
Browse files Browse the repository at this point in the history
…thod

feat(ts-model-api): add method for removing node
  • Loading branch information
odzhychko authored Jun 20, 2024
2 parents 941ab4c + c5bb1f6 commit e46081c
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useFakeNode } from "./test-helpers";

test("typed nodes can be removed", () => {
const { typedNode, rootNode } = useFakeNode();
expect(rootNode.getChildren("children1")).toHaveLength(1);

typedNode.remove();
expect(rootNode.getChildren("children1")).toHaveLength(0);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { org } from "@modelix/model-client";
import { LanguageRegistry } from "@modelix/ts-model-api";
import { registerLanguages } from "../build/typescript_src";

const DEFAULT_NODE_DATA = {
root: {
children: [
{
// concecpt ID of an PropertyAttribute
concept: "mps:ceab5195-25ea-4f22-9b92-103b95ca8c0c/3364660638048049750",
role: "children1",
properties: {
name: "aName",
},
},
],
},
};

export function useFakeRootNode(nodeData: object = DEFAULT_NODE_DATA) {
registerLanguages();

const { loadModelsFromJson } = org.modelix.model.client2;
const rootNode = loadModelsFromJson(
[JSON.stringify(nodeData)],
// for the purpose of the test a change handler is not needed
() => {}
);

function getUntypedNode(role: string = "children1") {
return rootNode.getChildren(role)[0];
}

function getTypedNode(role?: string) {
return LanguageRegistry.INSTANCE.wrapNode(getUntypedNode(role));
}

return {
rootNode,
getUntypedNode,
getTypedNode,
};
}

export function useFakeNode(role?: string, nodeData?: object) {
const { getUntypedNode, getTypedNode, rootNode } = useFakeRootNode(nodeData);
return {
rootNode,
untypedNode: getUntypedNode(role),
typedNode: getTypedNode(role),
};
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,11 @@
import { org } from "@modelix/model-client";
import { LanguageRegistry } from "@modelix/ts-model-api";
import {
isOfConcept_INamedConcept,
isOfConcept_PropertyAttribute,
isOfConcept_Attribute,
} from "../build/typescript_src/L_jetbrains_mps_lang_core";
import { registerLanguages } from "../build/typescript_src";
import { useFakeNode } from "./test-helpers";

registerLanguages();

const nodeData = {
root: {
children: [
{
// concecpt ID of an PropertyAttribute
concept: "mps:ceab5195-25ea-4f22-9b92-103b95ca8c0c/3364660638048049750",
role: "children1",
properties: {
name: "aName",
},
},
],
},
};

const untypedNode = useRootNode(nodeData).getChildren("children1")[0];
const typedNode = LanguageRegistry.INSTANCE.wrapNode(untypedNode);
const { typedNode } = useFakeNode();

test("verifies the concept of a node", () => {
expect(isOfConcept_PropertyAttribute(typedNode)).toBeTruthy();
Expand All @@ -43,12 +23,3 @@ test("nullish values are never of the type of the checked concept", () => {
expect(isOfConcept_INamedConcept(null)).toBeFalsy();
expect(isOfConcept_INamedConcept(undefined)).toBeFalsy();
});

function useRootNode(nodeData: object) {
const { loadModelsFromJson } = org.modelix.model.client2;
return loadModelsFromJson(
[JSON.stringify(nodeData)],
// for the purpose of the test a change handler is not needed
() => {}
);
}
1 change: 1 addition & 0 deletions model-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ kotlin {
}
val jsTest by getting {
dependencies {
implementation(project(":model-client"))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ class NodeAdapterJS(val node: INode) : INodeJS_ {
node.removeChild((child as NodeAdapterJS).node)
}

override fun remove() {
node.remove()
}

override fun getReferenceRoles(): Array<String> {
return node.getReferenceRoles().toTypedArray()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2023-2024.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.modelix.model.api

import org.modelix.model.ModelFacade
import kotlin.test.Test
import kotlin.test.assertEquals

class NodeAdapterJSTest {
@Test
fun nodesCanBeRemoved() {
val branch = ModelFacade.toLocalBranch(ModelFacade.newLocalTree())
val node = branch.computeWrite {
branch.getRootNode().addNewChild("roleOfTheChildThatGetsRemoved")
}
val jsNode = NodeAdapterJS(node)

jsNode.remove()

branch.computeRead {
assertEquals(0, branch.getRootNode().allChildren.count())
}
}
}
2 changes: 1 addition & 1 deletion ts-model-api/src/ChildrenAccessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class SingleChildAccessor<ChildT extends ITypedNode> extends ChildrenAcce
public setNew(): ChildT {
const existing = this.get();
if (existing !== undefined) {
this.parentNode.removeChild(existing.unwrap())
existing.remove();
}
return this.wrapChild(this.parentNode.addNewChild(this.role, 0, undefined))
}
Expand Down
2 changes: 2 additions & 0 deletions ts-model-api/src/INodeJS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export interface INodeJS {
getRoleInParent(): string | undefined
getParent(): INodeJS | undefined

remove(): void

getChildren(role: string | undefined): Array<INodeJS>
getAllChildren(): Array<INodeJS>
moveChild(role: string | undefined, index: number, child: INodeJS): void
Expand Down
4 changes: 4 additions & 0 deletions ts-model-api/src/TypedNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ export class TypedNode implements ITypedNode {
return this._node;
}

remove(): void {
this._node.remove();
}
}

export interface ITypedNode {
unwrap(): INodeJS
remove(): void
}

export class UnknownTypedNode extends TypedNode {
Expand Down
15 changes: 15 additions & 0 deletions vue-model-api/src/internal/ReactiveINodeJS.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,18 @@ test("change to all children is reactive", () => {
"child3",
]);
});

test("removing a node is reactive", () => {
const rootNode = useRootNode();
const childCount = rootNode.getChildren("children1").length;
const node = rootNode.getChildren("children1")[0];

// We use `computed` to test the reactivity with Vue.
// Accessing the property directly would circumvent Vue
// and make this test useless.
const computedProperty = computed(() => rootNode.getChildren("children1"));
expect(computedProperty.value).toHaveLength(childCount);

node.remove();
expect(computedProperty.value).toHaveLength(childCount - 1);
});
4 changes: 4 additions & 0 deletions vue-model-api/src/internal/ReactiveINodeJS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ export class ReactiveINodeJS implements INodeJS {
: unreacitveNode;
}

remove(): void {
this.unreactiveNode.remove();
}

getChildren(role: string | undefined): INodeJS[] {
const { track } = this.getOrCreateTrackAndTriggerForRole(role);
track();
Expand Down

0 comments on commit e46081c

Please sign in to comment.