mercurial/ui.py
changeset 27408 2916ebaef312
parent 27275 f2cd240f2f7c
parent 27392 00aa37c65e0a
child 27500 62b1b6a89d2a
--- a/mercurial/ui.py	Mon Dec 14 23:04:17 2015 +0000
+++ b/mercurial/ui.py	Thu Dec 17 17:27:32 2015 -0600
@@ -11,6 +11,7 @@
 import getpass
 import inspect
 import os
+import re
 import socket
 import sys
 import tempfile
@@ -812,10 +813,23 @@
 
         This returns tuple "(message, choices)", and "choices" is the
         list of tuple "(response character, text without &)".
+
+        >>> ui.extractchoices("awake? $$ &Yes $$ &No")
+        ('awake? ', [('y', 'Yes'), ('n', 'No')])
+        >>> ui.extractchoices("line\\nbreak? $$ &Yes $$ &No")
+        ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
+        >>> ui.extractchoices("want lots of $$money$$?$$Ye&s$$N&o")
+        ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
         """
-        parts = prompt.split('$$')
-        msg = parts[0].rstrip(' ')
-        choices = [p.strip(' ') for p in parts[1:]]
+
+        # Sadly, the prompt string may have been built with a filename
+        # containing "$$" so let's try to find the first valid-looking
+        # prompt to start parsing. Sadly, we also can't rely on
+        # choices containing spaces, ASCII, or basically anything
+        # except an ampersand followed by a character.
+        m = re.match(r'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
+        msg = m.group(1)
+        choices = [p.strip(' ') for p in m.group(2).split('$$')]
         return (msg,
                 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
                  for s in choices])