Repository: sphinx-doc/sphinx

Issue:
Wrong internal href fragment links in epub output
Let's take for example the Django settings documentation (https://docs.djangoproject.com/en/2.0/ref/settings).

In the HTML output, a text like ```:setting:`STATICFILES_FINDERS` ``` is translated to `<a class="reference internal" href="#std:setting-STATICFILES_FINDERS">` and the anchor is indeed matching that link.

In epub output, the link is `<a class="reference internal" href="#std-setting-STA     TICFILES_FINDERS">`, while the anchor is still `<span id="std:setting-STATICFILES_FINDERS">` (like for HTML). Therefore, the link doesn't work, because of the `-`/`:` mismatch in the link name.

Discussion:

Patch:
diff --git a/CHANGES b/CHANGES
index f3ebc0f42..6996c15b8 100644
--- a/CHANGES
+++ b/CHANGES
@@ -31,6 +31,7 @@ Bugs fixed
 * #5036: quickstart: Typing Ctrl-U clears the whole of line
 * #5066: html: "relations" sidebar is not shown by default
 * #5091: latex: curly braces in index entries are not handled correctly
+* #5070: epub: Wrong internal href fragment links
 
 Testing
 --------
diff --git a/sphinx/builders/_epub_base.py b/sphinx/builders/_epub_base.py
index 04812c56f..602a9e739 100644
--- a/sphinx/builders/_epub_base.py
+++ b/sphinx/builders/_epub_base.py
@@ -271,6 +271,16 @@ def fix_ids(self, tree):
                     node['refuri'] = self.fix_fragment(m.group(1), m.group(2))
             if 'refid' in node:
                 node['refid'] = self.fix_fragment('', node['refid'])
+        for node in tree.traverse(nodes.target):
+            for i, node_id in enumerate(node['ids']):
+                if ':' in node_id:
+                    node['ids'][i] = self.fix_fragment('', node_id)
+
+            next_node = node.next_node(siblings=True)
+            if next_node and isinstance(next_node, nodes.Element):
+                for i, node_id in enumerate(next_node['ids']):
+                    if ':' in node_id:
+                        next_node['ids'][i] = self.fix_fragment('', node_id)
         for node in tree.traverse(addnodes.desc_signature):
             ids = node.attributes['ids']
             newids = []


Test patch:
diff --git a/tests/roots/test-epub-anchor-id/conf.py b/tests/roots/test-epub-anchor-id/conf.py
new file mode 100644
index 000000000..3b73e0811
--- /dev/null
+++ b/tests/roots/test-epub-anchor-id/conf.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+
+master_doc = 'index'
+
+latex_documents = [
+    (master_doc, 'test.tex', 'The basic Sphinx documentation for testing', 'Sphinx', 'report')
+]
+
+
+def setup(app):
+    app.add_crossref_type(directivename="setting", rolename="setting")
diff --git a/tests/roots/test-epub-anchor-id/index.rst b/tests/roots/test-epub-anchor-id/index.rst
new file mode 100644
index 000000000..f3064615c
--- /dev/null
+++ b/tests/roots/test-epub-anchor-id/index.rst
@@ -0,0 +1,8 @@
+test-epub-anchor-id
+===================
+
+.. setting:: STATICFILES_FINDERS
+
+blah blah blah
+
+see :setting:`STATICFILES_FINDERS`
diff --git a/tests/test_build_epub.py b/tests/test_build_epub.py
index 2f09f6d5a..dae094028 100644
--- a/tests/test_build_epub.py
+++ b/tests/test_build_epub.py
@@ -317,6 +317,15 @@ def test_epub_writing_mode(app):
     assert 'writing-mode: vertical-rl;' in css
 
 
+@pytest.mark.sphinx('epub', testroot='epub-anchor-id')
+def test_epub_anchor_id(app):
+    app.build()
+
+    html = (app.outdir / 'index.xhtml').text()
+    assert '<p id="std-setting-STATICFILES_FINDERS">blah blah blah</p>' in html
+    assert 'see <a class="reference internal" href="#std-setting-STATICFILES_FINDERS">' in html
+
+
 @pytest.mark.sphinx('epub')
 def test_run_epubcheck(app):
     app.build()