Repository: sopel-irc/sopel

Issue:
dice: .choose output is messy
I started this thought process after noticing that `.choose thing1, thing2, thing3` will yield choices that include the leading space (`thing1`, ` thing2`, and ` thing3`), but it's not that simple.

Since `.choose` just uses a simple `str.split()` as-is, without cleaning the values at all, it's already starting with messy data. Then it swaps the delimiter used for no particular reason ([comment claims](https://github.com/sopel-irc/sopel/blob/d73a410717d6cb2b164bf5411f20a995210bbc22/sopel/modules/dice.py#L248) it's "to prevent ambiguity", but I think it actually does the opposite).

What comes out the other end:

```
<dgw> .choose thing1, thing2, thing3
<Sopel> dgw: Your options: thing1|  thing2|  thing3. My choice:  thing2
```

Extra spaces everywhere, delimiters that don't really make sense with the spacing around them… Ugly.

Current plans to sort this out:

  - [x] **In 6.6.0:** Clean the input to avoid extra spaces in the output. ~~Maybe switch to always use `, `&nbsp;as delimiter in output.~~ [#1421]
  - [x] **In 7.0.0:** ~~Drop support for everything but `,` (and maybe `/`) in the input, and~~ always use `, `&nbsp;in output ~~(if not already changed for 6.6.0)~~. [#1425]

There's considerable lead time before Sopel 7, so any user objections to dropping `\` and `|` as supported delimiters may be dropped in the comments here, or in the comments of the PR I will open with the change some time in early 2019, after 6.6.0 is out.


Discussion:
I noticed the extra spaces issue ~9M ago and fixed it with a one-liner :v https://github.com/alanhuang122/sopel/commit/97d8ae65c743638c7e8404295599e188004dd434.

I think using commas as exclusive delimiters in output (and quoting choices with commas internally) would work.

In my experience, the current system of splitting on `|\/,` works fine - many of my users
`.choose course of action 1, 2, 3 | course of action foo, bar, baz` - response is
`$nick: Your options: course of action 1, 2, 3/ course of action foo, bar, baz. My choice: course of action 1, 2, 3`

Yet another reason to just use commas - even non-comma delimiters get comma rules applied.
That one-liner is exactly what I'm planning to do, haha. Think I'll just cherry-pick that commit, if you don't mind. :cat: Or you can make a PR yourself by doing the same! But then, if I cherry-pick it, I can make a PR that builds on it with better output (see below).

Swapping delimiters around is what really gets me about the current code. It's just…ugly, most of the time, primarily due to spacing. The fact that you can put in `.ch option1, option2, option3` and get out `Your options: option1| option2| option3` (even with the `strip()` patch) is kind of absurd. Pipes and slashes look strange if the surrounding whitespace is imbalanced.

Maybe I won't take out the extra input separators… Maybe just making the output look better is enough. Maybe that can even be done for 6.6.0 (except I keep adding more and more to it, and it's already probably not coming out until next year because of that :joy_cat:).
I don't know that cherry-picking would go off without a hitch – I consolidated `dice.py` and `choose.py` into `rand.py` in my fork (There have been a lot of bastardizations in my fork). Feel free to simply include the modification, though.

re: formatting: As I alluded, indeed –
input delimiter options: :+1:
Output delimiter options: :grimacing:
Ooh, I didn't even notice the filename. Whoops. No excuse for that; I'm not even tired… 🤭 Fine then, I'll just do the same thing (as originally planned) in my own PR.
I was looking at the output part: https://github.com/dgw/sopel/blob/b869fb590c760aefb579de52629fed94bbd00d32/sopel/modules/dice.py#L256-L263

Maybe something like this could improve how it's displayed?

```
output_delimiters = [
    (',', '{delimiter} '),
    ('|', ' {delimiter} '),
    ('/', ' {delimiter} '),
    ('\\', ' {delimiter} '),
]
show_delim = ' '
for delimiter, template in output_delimiters:
    if delimiter not in trigger.group(2):
        show_delim = template.format(delimiter=delimiter)
        break
```

This way it would output:

```
<dgw> .choose thing1, thing2,thing3
<Sopel> dgw: Your options: thing1 | thing2 | thing3. My choice: thing2
<dgw> .choose thing1| thing2 |thing3
<Sopel> dgw: Your options: thing1, thing2, thing3. My choice: thing2
```

I could probably make a PR or something if you wish.

Resolving patch:
diff --git a/sopel/modules/dice.py b/sopel/modules/dice.py
--- a/sopel/modules/dice.py
+++ b/sopel/modules/dice.py
@@ -242,6 +242,15 @@ def _get_pretty_str(dice):
 @sopel.module.commands("ch")
 @sopel.module.commands("choose")
 @sopel.module.priority("medium")
+@sopel.module.example(".choose a, b, c", r'Your options: a, b, c. My choice: (a|b|c)', re=True)
+@sopel.module.example(".choose a | b | c", r'Your options: a, b, c. My choice: (a|b|c)', re=True)
+@sopel.module.example(".choose a,b,c", r'Your options: a, b, c. My choice: (a|b|c)', re=True)
+@sopel.module.example(".choose a|b|c", r'Your options: a, b, c. My choice: (a|b|c)', re=True)
+@sopel.module.example(".choose a b c", r'Your options: a, b, c. My choice: (a|b|c)', re=True)
+@sopel.module.example(".choose a, b | just a",
+                      r'Your options: "a, b", just a. My choice: ((a, b)|(just a))',
+                      re=True)
+@sopel.module.example(".choose a", 'Your options: a. My choice: a')
 def choose(bot, trigger):
     """
     .choice option1|option2|option3 - Makes a difficult choice easy.
@@ -249,19 +258,19 @@ def choose(bot, trigger):
     if not trigger.group(2):
         return bot.reply('I\'d choose an option, but you didn\'t give me any.')
     choices = [trigger.group(2)]
-    for delim in '|\\/,':
+    for delim in '|\\/, ':
         choices = trigger.group(2).split(delim)
         if len(choices) > 1:
             break
     choices = [choice.strip() for choice in choices]
-    # Use a different delimiter in the output, to prevent ambiguity.
-    for show_delim in ',|/\\':
-        if show_delim not in trigger.group(2):
-            show_delim += ' '
-            break
-
     pick = random.choice(choices)
-    return bot.reply('Your options: %s. My choice: %s' % (show_delim.join(choices), pick))
+
+    # Always use a comma in the output
+    display_options = ', '.join(
+        choice if ',' not in choice else '"%s"' % choice
+        for choice in choices
+    )
+    return bot.reply('Your options: %s. My choice: %s' % (display_options, pick))
 
 
 if __name__ == "__main__":


Test patch: