Repository: sphinx-gallery/sphinx-gallery

Issue:
Intro text gets truncated on '-' character
Hello,

We have a sample that we'd like to document, which has a sentence: "This is a Mach-Zehnder Interferometer used for [...]".

However, in gen_rst.py, extract_intro_and_title, the regex defined is this:

`match = re.search(r'([\w ]+)', title_paragraph)`

This will truncate on the '-' sign because of this specific regex. Would it be safe/ok to add the '-' symbol here, too? I can imagine there will be other sentences where a hyphen will be present.

Thanks for this nice project, btw!

Comments:

Patch:
diff --git a/doc/advanced.rst b/doc/advanced.rst
index 0e912be..c3bc542 100644
--- a/doc/advanced.rst
+++ b/doc/advanced.rst
@@ -83,11 +83,39 @@ Image scrapers are functions (or callable class instances) that do two things:
    extensions, respectively)
 3. Return rST that embeds these figures in the built documentation.
 
-The function should take the following inputs (in this order): ``block``,
-``block_vars``, and ``gallery_conf``. It should return a string containing the
-rST for embedding this figure in the documentation.
-See :func:`~sphinx_gallery.scrapers.matplotlib_scraper` for
-a description of the inputs/outputs.
+The function should take the following inputs (in this order):
+
+1. ``block`` - a Sphinx-Gallery ``.py`` file is separated into consecutive
+   lines of 'code' and rST 'text', called 'blocks'. For each
+   block, a tuple containing the (label, content, line_number) 
+   (e.g. ``('code', 'print("Hello world")', 5)``) of the block is created.
+
+   * 'label' is a string that can either be ``'text'`` or ``'code'``. In this
+     context, it should only be ``'code'`` as this function is only called for
+     code blocks.
+   * 'content' is a string containing the actual content of the code block.
+   * 'line_number' is an integer, indicating the line number that the block
+     starts at.
+
+2. ``block_vars`` - dictionary of configuration and runtime variables. Of
+   interest for image scrapers is the element ``'image_path_iterator'`` which
+   is an iterable object which returns an absolute path to an image file name
+   adhering to Sphinx-Gallery naming convention. The path directs to the 
+   ``gallery_dirs/images`` directory (:ref:`configure_and_use_sphinx_gallery`)
+   and the image file name is ``'sphx_glr_'`` followed by the name of the 
+   source ``.py`` file then a number, which starts at 1 and increases by 1 at
+   each iteration. The default file format is ``.'png'``. For example:
+   ``'home/user/Documents/module/auto_examples/images/sphx_glr_plot_mymodule_001.png'``
+   
+3. ``gallery_conf`` - dictionary containing the configuration of Sphinx-Gallery,
+   set under ``sphinx_gallery_conf`` in ``doc/conf.py`` (:ref:`configuration`).
+
+It should return a string containing the rST for embedding this figure in the
+documentation. See :func:`~sphinx_gallery.scrapers.matplotlib_scraper` for an
+example of a scraper function (click on 'source' below the function name to see
+the source code). The :func:`~sphinx_gallery.scrapers.matplotlib_scraper` uses
+the helper function :func:`sphinx_gallery.scrapers.figure_rst` to help generate
+rST (see below).
 
 This function will be called once for each code block of your examples.
 Sphinx-gallery will take care of scaling images for the gallery
diff --git a/doc/getting_started.rst b/doc/getting_started.rst
index 43356a7..e613497 100644
--- a/doc/getting_started.rst
+++ b/doc/getting_started.rst
@@ -94,6 +94,8 @@ this folder must have the following things:
       included as sub-sections of your gallery. They **must** contain their own
       ``README.txt`` or ``README.rst`` file as well.
 
+.. _configure_and_use_sphinx_gallery:
+
 Configure and use sphinx-gallery
 --------------------------------
 
diff --git a/doc/syntax.rst b/doc/syntax.rst
index b0e7ae7..a4962d8 100644
--- a/doc/syntax.rst
+++ b/doc/syntax.rst
@@ -13,11 +13,13 @@ A simple example
 Sphinx-Gallery expects each Python file to have two things:
 
 1. **A docstring**, written in rST, that defines the
-   header for the example. It must begin by defining an rST title. For example::
+   header for the example. It must begin by defining a rST title. The title
+   may contain any punctuation mark but cannot start with the same punctuation
+   mark repeated more than 3 times. For example::
 
     """
-    This is my example script
-    =========================
+    "This" is my example-script
+    ===========================
 
     This example doesn't do much, it just makes a simple plot
     """
diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py
index f74bf18..3e635b8 100644
--- a/sphinx_gallery/gen_rst.py
+++ b/sphinx_gallery/gen_rst.py
@@ -186,16 +186,18 @@ def extract_intro_and_title(filename, docstring):
             "Example docstring should have a header for the example title. "
             "Please check the example file:\n {}\n".format(filename))
     # Title is the first paragraph with any ReSTructuredText title chars
-    # removed, i.e. lines that consist of (all the same) 7-bit non-ASCII chars.
+    # removed, i.e. lines that consist of (3 or more of the same) 7-bit
+    # non-ASCII chars.
     # This conditional is not perfect but should hopefully be good enough.
     title_paragraph = paragraphs[0]
-    match = re.search(r'([\w ]+)', title_paragraph)
+    match = re.search(r'^(?!([\W _])\1{3,})(.+)', title_paragraph,
+                      re.MULTILINE)
 
     if match is None:
         raise ValueError(
             'Could not find a title in first paragraph:\n{}'.format(
                 title_paragraph))
-    title = match.group(1).strip()
+    title = match.group(0).strip()
     # Use the title if no other paragraphs are provided
     intro_paragraph = title if len(paragraphs) < 2 else paragraphs[1]
     # Concatenate all lines of the first paragraph and truncate at 95 chars


Test patch:
diff --git a/sphinx_gallery/tests/test_gen_rst.py b/sphinx_gallery/tests/test_gen_rst.py
index 11c6553..b5cda51 100644
--- a/sphinx_gallery/tests/test_gen_rst.py
+++ b/sphinx_gallery/tests/test_gen_rst.py
@@ -210,6 +210,11 @@ def test_extract_intro_and_title():
                                               '^^^^^\n   Title  two  \n^^^^^')
     assert intro == title == 'Title  two'
 
+    # Title with punctuation (gh-517)
+    intro, title = sg.extract_intro_and_title('<string>',
+                                              '    ------------\n"-`Header"-with:; `punct` mark\'s\n----------------')
+    assert title == '"-`Header"-with:; `punct` mark\'s'
+
     # Long intro paragraph gets shortened
     intro_paragraph = '\n'.join(['this is one line' for _ in range(10)])
     intro, _ = sg.extract_intro_and_title(
@@ -224,7 +229,7 @@ def test_extract_intro_and_title():
     with pytest.raises(ValueError, match='should have a header'):
         sg.extract_intro_and_title('<string>', '')  # no title
     with pytest.raises(ValueError, match='Could not find a title'):
-        sg.extract_intro_and_title('<string>', '-')  # no real title
+        sg.extract_intro_and_title('<string>', '=====')  # no real title
 
 
 def test_md5sums():