Repository: certbot/certbot

Issue:
Tweak output to not trigger markdown heading status
We have a lot of horizontal lines of dashes in the Certbot output, e.g.

```
Which names would you like to activate HTTPS for?
-------------------------------------------------------------------------------
1: example.com
2: www.example.com
-------------------------------------------------------------------------------
```

Unfortunately, Markdown interprets any text above one of these lines as a heading, leading to distracting and weird formatting when someone pastes it into the forum:

https://community.letsencrypt.org/t/certbot-apache-succeeded-now-site-times-out/63168/3

Arguably they should wrap things in triple-backticks, but this happens quite a lot: People who need help on the forum often don't take time to learn about Markdown. It would be nice to make a small tweak so that formatting just works correctly. Unfortunately, lines of underscores, plus signs, colons, or equal signs all have the same effect. One simple tweak that works is inserting a space (or multiple spaces) in the line of hyphens:

    - -------------------------------------------------------------------------------

or

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



Discussion:
Seems reasonable to me.

I personally have a preference for:
```
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
```
but I'm open to other approaches.

The fix for this would go into `certbot/display/util.py`.
mind if I take this for a short demo at an open-source workshop next friday?
Go for it!

Resolving patch:
diff --git a/certbot/display/util.py b/certbot/display/util.py
--- a/certbot/display/util.py
+++ b/certbot/display/util.py
@@ -29,6 +29,10 @@
 ESC = "esc"
 """Display exit code when the user hits Escape (UNUSED)"""
 
+# Display constants
+SIDE_FRAME = ("- " * 39) + "-"
+"""Display boundary (alternates spaces, so when copy-pasted, markdown doesn't interpret
+it as a heading)"""
 
 def _wrap_lines(msg):
     """Format lines nicely to 80 chars.
@@ -111,12 +115,11 @@ def notification(self, message, pause=True,
             because it won't cause any workflow regressions
 
         """
-        side_frame = "-" * 79
         if wrap:
             message = _wrap_lines(message)
         self.outfile.write(
             "{line}{frame}{line}{msg}{line}{frame}{line}".format(
-                line=os.linesep, frame=side_frame, msg=message))
+                line=os.linesep, frame=SIDE_FRAME, msg=message))
         self.outfile.flush()
         if pause:
             if self._can_interact(force_interactive):
@@ -208,12 +211,10 @@ def yesno(self, message, yes_label="Yes", no_label="No", default=None,
         if self._return_default(message, default, cli_flag, force_interactive):
             return default
 
-        side_frame = ("-" * 79) + os.linesep
-
         message = _wrap_lines(message)
 
         self.outfile.write("{0}{frame}{msg}{0}{frame}".format(
-            os.linesep, frame=side_frame, msg=message))
+            os.linesep, frame=SIDE_FRAME + os.linesep, msg=message))
         self.outfile.flush()
 
         while True:
@@ -386,8 +387,7 @@ def _print_menu(self, message, choices):
         # Write out the message to the user
         self.outfile.write(
             "{new}{msg}{new}".format(new=os.linesep, msg=message))
-        side_frame = ("-" * 79) + os.linesep
-        self.outfile.write(side_frame)
+        self.outfile.write(SIDE_FRAME + os.linesep)
 
         # Write out the menu choices
         for i, desc in enumerate(choices, 1):
@@ -397,7 +397,7 @@ def _print_menu(self, message, choices):
             # Keep this outside of the textwrap
             self.outfile.write(os.linesep)
 
-        self.outfile.write(side_frame)
+        self.outfile.write(SIDE_FRAME + os.linesep)
         self.outfile.flush()
 
     def _get_valid_int_ans(self, max_):
@@ -482,12 +482,11 @@ def notification(self, message, pause=False, wrap=True, **unused_kwargs):
         :param bool wrap: Whether or not the application should wrap text
 
         """
-        side_frame = "-" * 79
         if wrap:
             message = _wrap_lines(message)
         self.outfile.write(
             "{line}{frame}{line}{msg}{line}{frame}{line}".format(
-                line=os.linesep, frame=side_frame, msg=message))
+                line=os.linesep, frame=SIDE_FRAME, msg=message))
         self.outfile.flush()
 
     def menu(self, message, choices, ok_label=None, cancel_label=None,


Test patch: