diff --git a/orangecanvas/document/schemeedit.py b/orangecanvas/document/schemeedit.py index 8530df38..57f3d540 100644 --- a/orangecanvas/document/schemeedit.py +++ b/orangecanvas/document/schemeedit.py @@ -9,6 +9,7 @@ import io import logging import itertools +import re import sys import unicodedata import copy @@ -2169,10 +2170,14 @@ def __paste(self, nodedups, linkdups, pos: Optional[QPointF] = None, return # find unique names for new nodes - allnames = {node.title for node in scheme.nodes + nodedups} + allnames = {node.title for node in scheme.nodes} + for nodedup in nodedups: nodedup.title = uniquify( - nodedup.title, allnames, pattern="{item} ({_})", start=1) + remove_copy_number(nodedup.title), allnames, + pattern="{item} ({_})", start=1 + ) + allnames.add(nodedup.title) if pos is not None: # top left of nodedups brect @@ -2551,11 +2556,25 @@ def can_insert_node(new_node_desc, original_link): for input in original_link.sink_node.input_channels()) +def remove_copy_number(name): + """ + >>> remove_copy_number("foo (1)") + foo + """ + match = re.search(r"\s+\(\d+\)\s*$", name) + if match: + return name[:match.start()] + return name + + def uniquify(item, names, pattern="{item}-{_}", start=0): # type: (str, Container[str], str, int) -> str candidates = (pattern.format(item=item, _=i) for i in itertools.count(start)) - candidates = itertools.dropwhile(lambda item: item in names, candidates) + candidates = itertools.dropwhile( + lambda item: item in names, + itertools.chain((item,), candidates) + ) return next(candidates) diff --git a/orangecanvas/document/tests/test_schemeedit.py b/orangecanvas/document/tests/test_schemeedit.py index 4e9da783..bbc97e16 100644 --- a/orangecanvas/document/tests/test_schemeedit.py +++ b/orangecanvas/document/tests/test_schemeedit.py @@ -1,6 +1,7 @@ """ Tests for scheme document. """ +import re import sys import unittest from unittest import mock @@ -408,6 +409,15 @@ def test_duplicate(self): a.trigger() self.assertEqual(len(workflow.nodes), 2 * nnodes) self.assertEqual(len(workflow.links), 2 * nlinks) + w.selectAll() + a.trigger() + self.assertEqual(len(workflow.nodes), 4 * nnodes) + self.assertEqual(len(workflow.links), 4 * nlinks) + self.assertEqual(len(workflow.nodes), + len(set(n.title for n in workflow.nodes))) + match = re.compile(r"\(\d+\)\s*\(\d+\)") + self.assertFalse(any(match.search(n.title) for n in workflow.nodes), + "Duplicated renumbering ('foo (2) (1)')") def test_copy_paste(self): w = self.w