Repository: napari/napari

Issue:
novel gene discovery in Napari  (a.k.a. imperfect layer name incrementing)
## 🐛 Bug
napari does a smart thing and adds an integer to the name of a layer if the name is already in use in the viewer. It then increments that number when another layer is added that fits the pattern.  This is great until you have layer names that end in numbers 
![Screenshot from 2019-10-13 13-28-55](https://user-images.githubusercontent.com/6773896/66722077-6379b300-edbe-11e9-91e3-37784cc7aa70.png)
which in this case results in different names that are sometimes real genes and sometimes not.

## To Reproduce

Steps to reproduce the behavior:
```
v = napari.Viewer()
for i in range(3):
    v.add_points(1000*np.random.rand(10,2), name = "no_problem")
for i in range(3):
    v.add_points(1000*np.random.rand(10,2), name = "problem:GAD1")
```

## Expected behavior
consistent use of a space or other character to separate the auto-increment integer from the original string



Discussion:
ok - we can check if there is a spacebar before the last number and if no then we auto increment with `1` separated by a space, and if yes then we bump that number `+=1`. So for you
`GAD1` x3 => `GAD1`, `GAD1 1`, `GAD1 2`
but if you have done
`GAD 1` x3 => `GAD 1`, `GAD 2`, `GAD 3`
seem reasonable? that should be an easy change
Yikes, next thing you know we'll be interpreting gene names as dates a-la Excel... =P

Another option would be to add parentheses or square brackets: Image [1], Image [2], ...

btw @berl that image is gorgeous, any chance we could use it for our examples? =D
I like `Image [1]` better than `Image 1` makes it more clear it was auto-incremented
we'd then have
```
GAD1 x3 => GAD1, GAD1 [1], GAD1 [2]
GAD 1 x3 => GAD 1, GAD 1 [1], GAD 1 [2]
GAD [1] x3 => GAD [1], GAD [2], GAD [3]
Image x3 => Image, Image [1], Image [2]
```
which is probably the most clear

Resolving patch:
diff --git a/napari/util/naming.py b/napari/util/naming.py
--- a/napari/util/naming.py
+++ b/napari/util/naming.py
@@ -7,7 +7,9 @@
 sep = ' '
 start = 1
 
-numbered_patt = re.compile(r'(?<!\d)(?:\d+|)$')
+# Match integer between square brackets at end of string if after space
+# or at beginning of string or just match end of string
+numbered_patt = re.compile(r'((?<=\A\[)|(?<=\s\[))(?:\d+|)(?=\]$)|$')
 
 
 def _inc_name_count_sub(match):
@@ -16,7 +18,7 @@ def _inc_name_count_sub(match):
     try:
         count = int(count)
     except ValueError:  # not an int
-        count = f'{sep}{start}'
+        count = f'{sep}[{start}]'
     else:
         count = f'{count + 1}'
 
@@ -27,7 +29,7 @@ def _inc_name_count_sub(match):
 def inc_name_count(name):
     """Increase a name's count matching `{numbered_patt}` by ``1``.
 
-    If the name is not already numbered, append '{sep}{start}'.
+    If the name is not already numbered, append '{sep}[{start}]'.
 
     Parameters
     ----------
@@ -39,4 +41,4 @@ def inc_name_count(name):
     incremented_name : str
         Numbered name incremented by ``1``.
     """
-    return numbered_patt.sub(_inc_name_count_sub, name)
+    return numbered_patt.sub(_inc_name_count_sub, name, count=1)


Test patch:
diff --git a/napari/components/tests/test_layers_list.py b/napari/components/tests/test_layers_list.py
--- a/napari/components/tests/test_layers_list.py
+++ b/napari/components/tests/test_layers_list.py
@@ -111,13 +111,13 @@ def test_naming():
     layers.append(layer_a)
     layers.append(layer_b)
 
-    assert [l.name for l in layers] == ['img', 'img 1']
+    assert [l.name for l in layers] == ['img', 'img [1]']
 
     layer_b.name = 'chg'
     assert [l.name for l in layers] == ['img', 'chg']
 
     layer_a.name = 'chg'
-    assert [l.name for l in layers] == ['chg 1', 'chg']
+    assert [l.name for l in layers] == ['chg [1]', 'chg']
 
 
 def test_selection():
diff --git a/napari/components/tests/test_multichannel.py b/napari/components/tests/test_multichannel.py
--- a/napari/components/tests/test_multichannel.py
+++ b/napari/components/tests/test_multichannel.py
@@ -40,9 +40,7 @@ def test_names():
 
     viewer = ViewerModel()
     name = 'example'
-    names = [name] + [
-        name + ' ' + str(i + 1) for i in range(data.shape[-1] - 1)
-    ]
+    names = [name] + [name + f' [{i + 1}]' for i in range(data.shape[-1] - 1)]
     viewer.add_multichannel(data, name=name)
     assert len(viewer.layers) == data.shape[-1]
     for i in range(data.shape[-1]):
diff --git a/napari/util/tests/test_naming.py b/napari/util/tests/test_naming.py
--- a/napari/util/tests/test_naming.py
+++ b/napari/util/tests/test_naming.py
@@ -1,35 +1,35 @@
 from ..naming import numbered_patt, inc_name_count, sep, start
 
 
-def test_re_base_num():
-    assert numbered_patt.search('layer 12').group(0) == '12'
-
-
-def test_re_base_empty():
+def test_re_base_brackets():
+    assert numbered_patt.search('layer [12]').group(0) == '12'
+    assert numbered_patt.search('layer [e]').group(0) == ''
+    assert numbered_patt.search('layer 12]').group(0) == ''
+    assert numbered_patt.search('layer [12').group(0) == ''
+    assert numbered_patt.search('layer[12]').group(0) == ''
+    assert numbered_patt.search('layer 12').group(0) == ''
+    assert numbered_patt.search('layer12').group(0) == ''
     assert numbered_patt.search('layer').group(0) == ''
 
 
-def test_re_other_digs():
-    assert numbered_patt.search('layer3 123').group(0) == '123'
-
-
-def test_re_no_space():
-    assert numbered_patt.search('layer7').group(0) == '7'
+def test_re_other_brackets():
+    assert numbered_patt.search('layer [3] [123]').group(0) == '123'
 
 
-def test_re_first_dig():
-    assert numbered_patt.search('42').group(0) == '42'
+def test_re_first_bracket():
+    assert numbered_patt.search(' [42]').group(0) == '42'
+    assert numbered_patt.search('[42]').group(0) == '42'
 
 
 def test_re_sub_base_num():
-    assert numbered_patt.sub('8', 'layer 7') == 'layer 8'
+    assert numbered_patt.sub('8', 'layer [7]', count=1) == 'layer [8]'
 
 
 def test_re_sub_base_empty():
-    assert numbered_patt.sub(' 3', 'layer') == 'layer 3'
+    assert numbered_patt.sub(' [3]', 'layer', count=1) == 'layer [3]'
 
 
 def test_inc_name_count():
-    assert inc_name_count('layer 7') == 'layer 8'
-    assert inc_name_count('layer') == f'layer{sep}{start}'
-    assert inc_name_count('41') == '42'
+    assert inc_name_count('layer [7]') == 'layer [8]'
+    assert inc_name_count('layer') == f'layer{sep}[{start}]'
+    assert inc_name_count('[41]') == '[42]'