From 41f20ee9a2f3195fca5e0fa38f19d1302d31f36a Mon Sep 17 00:00:00 2001 From: ap891843 Date: Fri, 16 Aug 2024 14:42:43 +0200 Subject: [PATCH] fix: adjust copy node at right location in the parent's child list structure Signed-off-by: ap891843 test: add test as suggested in PR#2439 Signed-off-by: ap891843 --- ...otNodeUpdateCopyNodesByPositionInTree.java | 39 +++++- ...deUpdateCopyNodesByPositionInTreeTest.java | 132 ++++++++++++++++++ ...tCopynodeInsertionAtCorrectPlaceInAST.java | 65 +++++++++ 3 files changed, 231 insertions(+), 5 deletions(-) create mode 100644 server/engine/src/test/java/org/eclipse/lsp/cobol/core/engine/processors/RootNodeUpdateCopyNodesByPositionInTreeTest.java create mode 100644 server/engine/src/test/java/org/eclipse/lsp/cobol/usecases/TestCopynodeInsertionAtCorrectPlaceInAST.java diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/RootNodeUpdateCopyNodesByPositionInTree.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/RootNodeUpdateCopyNodesByPositionInTree.java index 32dca759bf..eb92420aed 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/RootNodeUpdateCopyNodesByPositionInTree.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/RootNodeUpdateCopyNodesByPositionInTree.java @@ -22,6 +22,7 @@ import org.eclipse.lsp.cobol.common.model.tree.RootNode; import org.eclipse.lsp.cobol.common.utils.RangeUtils; import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Range; import java.util.List; @@ -39,11 +40,13 @@ private void updateCopyNodes(RootNode node) { node.getChildren().stream().filter(Node.hasType(NodeType.COPY)).collect(toList()); nodes.forEach(node::removeChild); nodes.forEach( - it -> - RangeUtils.findNodeByPosition( - node, it.getLocality().getUri(), it.getLocality().getRange().getStart()) - .orElse(node) - .addChild(it)); + it -> { + Node parentNode = + RangeUtils.findNodeByPosition( + node, it.getLocality().getUri(), it.getLocality().getRange().getStart()) + .orElse(node); + parentNode.addChildAt(getNodeInsertionIndex(parentNode.getChildren(), it), it); + }); List copyNodes = node.getDepthFirstStream() .filter(Node.hasType(NodeType.COPY)) @@ -53,6 +56,32 @@ private void updateCopyNodes(RootNode node) { copyNodes.forEach(c -> registerCopyUsage(copyNodes, c.getNameLocation(), c.getUri())); } + private int getNodeInsertionIndex(List nodes, Node nodeToInsert) { + int nodeSize = nodes.size(); + String nodeToInsertUri = nodeToInsert.getLocality().getUri(); + Range nodeToInsertRange = nodeToInsert.getLocality().getRange(); + int nodeToInsertLine = nodeToInsertRange.getStart().getLine(); + int nodeToInsertCharacter = nodeToInsertRange.getStart().getCharacter(); + + for (int index = 0; index < nodeSize; index++) { + Node currentNode = nodes.get(index); + if (currentNode.getLocality().getUri().equals(nodeToInsertUri)) { + Range currentNodeRange = currentNode.getLocality().getRange(); + int currentNodeLine = currentNodeRange.getStart().getLine(); + int currentNodeCharacter = currentNodeRange.getStart().getCharacter(); + + if (currentNodeLine == nodeToInsertLine) { + if (currentNodeCharacter > nodeToInsertCharacter) { + return index; + } + } else if (currentNodeLine > nodeToInsertLine) { + return index; + } + } + } + return nodeSize; + } + private void registerCopyUsage(List node, Location nameLocation, String uri) { node.forEach(n -> { if (n.getUri() != null && !n.getNameLocation().equals(nameLocation) && n.getUri().equals(uri)) { diff --git a/server/engine/src/test/java/org/eclipse/lsp/cobol/core/engine/processors/RootNodeUpdateCopyNodesByPositionInTreeTest.java b/server/engine/src/test/java/org/eclipse/lsp/cobol/core/engine/processors/RootNodeUpdateCopyNodesByPositionInTreeTest.java new file mode 100644 index 0000000000..f3447cdcd2 --- /dev/null +++ b/server/engine/src/test/java/org/eclipse/lsp/cobol/core/engine/processors/RootNodeUpdateCopyNodesByPositionInTreeTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2024 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + * + */ +package org.eclipse.lsp.cobol.core.engine.processors; + +import org.eclipse.lsp.cobol.common.model.Locality; +import org.eclipse.lsp.cobol.common.model.SectionType; +import org.eclipse.lsp.cobol.common.model.tree.*; +import org.eclipse.lsp.cobol.common.model.tree.statements.StatementNode; +import org.eclipse.lsp.cobol.common.processor.ProcessingContext; +import org.eclipse.lsp.cobol.common.symbols.VariableAccumulator; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.Objects; +import java.util.stream.Collectors; + +import static org.mockito.Mockito.mock; + +/** + * Tests {@link RootNodeUpdateCopyNodesByPositionInTreeTest} + */ +class RootNodeUpdateCopyNodesByPositionInTreeTest { + + public static final String DOC_URI = "uri"; + public static final String COPYBOOK_NAME = "copybook-A"; + + @Test + void accept() { + RootNodeUpdateCopyNodesByPositionInTree copyNodesProcessor = + new RootNodeUpdateCopyNodesByPositionInTree(); + RootNode rootNode = getMockedRootNode(); + + Node copyNode = + new CopyNode( + Locality.builder() + .uri(DOC_URI) + .range(new Range(new Position(2, 3), new Position(2, COPYBOOK_NAME.length() + 3))) + .build(), + new Location( + DOC_URI, + new Range(new Position(2, 5), new Position(2, COPYBOOK_NAME.length() + 3))), + COPYBOOK_NAME, + DOC_URI); + rootNode.addChild(copyNode); + + VariableAccumulator variableAccumulator = mock(VariableAccumulator.class); + copyNodesProcessor.accept( + rootNode, + new ProcessingContext( + Collections.emptyList(), variableAccumulator, Collections.emptyMap())); + + Assertions.assertEquals(getStructure(getExpectedRootNode(copyNode)), getStructure(rootNode)); + } + + private static String getStructure(RootNode rootNode) { + return rootNode + .getDepthFirstStream() + .map(Node::getNodeType) + .map(Objects::toString) + .collect(Collectors.joining("-->")); + } + + private RootNode getExpectedRootNode(Node copyNode) { + RootNode rootNode = new RootNode(); + Node programNode = + new ProgramNode( + Locality.builder() + .uri(DOC_URI) + .range(new Range(new Position(1, 0), new Position(5, 7))) + .build(), ProgramSubtype.Program, 0); + SectionNode sectionNode = + new SectionNode( + Locality.builder() + .uri(DOC_URI) + .range(new Range(new Position(2, 2), new Position(5, 7))) + .build(), + SectionType.PROCEDURE); + // expect to get the copynode adjusted at right location + sectionNode.addChild(copyNode); + sectionNode.addChild( + new StatementNode( + Locality.builder() + .uri(DOC_URI) + .range(new Range(new Position(3, 2), new Position(4, 7))) + .build())); + programNode.addChild(sectionNode); + rootNode.addChild(programNode); + return rootNode; + } + + private RootNode getMockedRootNode() { + RootNode rootNode = new RootNode(); + Node programNode = + new ProgramNode( + Locality.builder() + .uri(DOC_URI) + .range(new Range(new Position(1, 0), new Position(5, 7))) + .build(), ProgramSubtype.Program, 0); + SectionNode sectionNode = + new SectionNode( + Locality.builder() + .uri(DOC_URI) + .range(new Range(new Position(2, 2), new Position(5, 7))) + .build(), + SectionType.PROCEDURE); + sectionNode.addChild( + new StatementNode( + Locality.builder() + .uri(DOC_URI) + .range(new Range(new Position(3, 2), new Position(4, 7))) + .build())); + programNode.addChild(sectionNode); + rootNode.addChild(programNode); + return rootNode; + } +} diff --git a/server/engine/src/test/java/org/eclipse/lsp/cobol/usecases/TestCopynodeInsertionAtCorrectPlaceInAST.java b/server/engine/src/test/java/org/eclipse/lsp/cobol/usecases/TestCopynodeInsertionAtCorrectPlaceInAST.java new file mode 100644 index 0000000000..2c9e9350c1 --- /dev/null +++ b/server/engine/src/test/java/org/eclipse/lsp/cobol/usecases/TestCopynodeInsertionAtCorrectPlaceInAST.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + * + */ +package org.eclipse.lsp.cobol.usecases; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.eclipse.lsp.cobol.common.AnalysisResult; +import org.eclipse.lsp.cobol.common.model.tree.Node; +import org.eclipse.lsp.cobol.test.CobolText; +import org.eclipse.lsp.cobol.test.engine.UseCaseEngine; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Objects; +import java.util.stream.Collectors; + +/** Test CopyNode position in AST when it contains CICS or SQL statements */ +public class TestCopynodeInsertionAtCorrectPlaceInAST { + public static final String TEXT = + " IDENTIFICATION DIVISION.\n" + + " PROGRAM-ID. CBACT01C.\n" + + " DATA DIVISION.\n" + + " WORKING-STORAGE SECTION.\n" + + " 01 {$*SOME-VAR} pic x.\n" + + " 01 {$*SOME_VAR_2} pic x.\n" + + " PROCEDURE DIVISION.\n" + + " IF {$SOME-VAR} > {$SOME_VAR_2} " + + " PERFORM {#HANDLE-ABEND}\n" + + " COPY {~COPY1}.\n" + + " DISPLAY '1'\n" + + " END-IF.\n" + + " {#*HANDLE-ABEND}."; + + public static final String COPYBOOK_CONTENT = + " EXEC CICS HANDLE ABEND LABEL({#HANDLE-ABEND}) END-EXEC"; + + @Test + void test() { + AnalysisResult result = + UseCaseEngine.runTest( + TEXT, ImmutableList.of(new CobolText("COPY1", COPYBOOK_CONTENT)), ImmutableMap.of()); + + String astFlatStructure = + result + .getRootNode() + .getDepthFirstStream() + .map(Node::getNodeType) + .map(Objects::toString) + .collect(Collectors.joining("-->")); + Assertions.assertTrue( + astFlatStructure.matches(".*SENTENCE-->IF-->.*?-->PERFORM-->.*?-->COPY-->STATEMENT.*")); + } +}