We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
full links not recognized
**Describe the bug**
Only the first part of a link with a /@ in it is being linked so https://mastodon.social/@username only auto links https://mastodon.social and strips '@username' from being a link

**To Reproduce**
here is an example https://bookwyrm.social/user/wakest/comment/30867

**Expected behavior**
When a link is in a comment, the whole link should be turned into a link not just part of it

**Screenshots**
<img width="858" alt="image" src="https://user-images.githubusercontent.com/7890201/124171841-ae45cd80-da6e-11eb-9071-a74596df184a.png">

**Instance**
https://bookwyrm.social

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bookwyrm/views/status.py`
Content:
```
1 """ what are we here for if not for posting """
2 import re
3 from django.contrib.auth.decorators import login_required
4 from django.http import HttpResponseBadRequest
5 from django.shortcuts import get_object_or_404, redirect
6 from django.template.response import TemplateResponse
7 from django.utils.decorators import method_decorator
8 from django.views import View
9 from markdown import markdown
10 
11 from bookwyrm import forms, models
12 from bookwyrm.sanitize_html import InputHtmlParser
13 from bookwyrm.settings import DOMAIN
14 from bookwyrm.utils import regex
15 from .helpers import handle_remote_webfinger
16 from .reading import edit_readthrough
17 
18 
19 # pylint: disable= no-self-use
20 @method_decorator(login_required, name="dispatch")
21 class CreateStatus(View):
22     """the view for *posting*"""
23 
24     def get(self, request, status_type):  # pylint: disable=unused-argument
25         """compose view (used for delete-and-redraft"""
26         book = get_object_or_404(models.Edition, id=request.GET.get("book"))
27         data = {"book": book}
28         return TemplateResponse(request, "compose.html", data)
29 
30     def post(self, request, status_type):
31         """create  status of whatever type"""
32         status_type = status_type[0].upper() + status_type[1:]
33 
34         try:
35             form = getattr(forms, "%sForm" % status_type)(request.POST)
36         except AttributeError:
37             return HttpResponseBadRequest()
38         if not form.is_valid():
39             return redirect(request.headers.get("Referer", "/"))
40 
41         status = form.save(commit=False)
42         if not status.sensitive and status.content_warning:
43             # the cw text field remains populated when you click "remove"
44             status.content_warning = None
45         status.save(broadcast=False)
46 
47         # inspect the text for user tags
48         content = status.content
49         for (mention_text, mention_user) in find_mentions(content):
50             # add them to status mentions fk
51             status.mention_users.add(mention_user)
52 
53             # turn the mention into a link
54             content = re.sub(
55                 r"%s([^@]|$)" % mention_text,
56                 r'<a href="%s">%s</a>\g<1>' % (mention_user.remote_id, mention_text),
57                 content,
58             )
59         # add reply parent to mentions
60         if status.reply_parent:
61             status.mention_users.add(status.reply_parent.user)
62 
63         # deduplicate mentions
64         status.mention_users.set(set(status.mention_users.all()))
65 
66         # don't apply formatting to generated notes
67         if not isinstance(status, models.GeneratedNote) and content:
68             status.content = to_markdown(content)
69         # do apply formatting to quotes
70         if hasattr(status, "quote"):
71             status.quote = to_markdown(status.quote)
72 
73         status.save(created=True)
74 
75         # update a readthorugh, if needed
76         edit_readthrough(request)
77 
78         return redirect("/")
79 
80 
81 @method_decorator(login_required, name="dispatch")
82 class DeleteStatus(View):
83     """tombstone that bad boy"""
84 
85     def post(self, request, status_id):
86         """delete and tombstone a status"""
87         status = get_object_or_404(models.Status, id=status_id)
88 
89         # don't let people delete other people's statuses
90         if status.user != request.user and not request.user.has_perm("moderate_post"):
91             return HttpResponseBadRequest()
92 
93         # perform deletion
94         status.delete()
95         return redirect(request.headers.get("Referer", "/"))
96 
97 
98 @method_decorator(login_required, name="dispatch")
99 class DeleteAndRedraft(View):
100     """delete a status but let the user re-create it"""
101 
102     def post(self, request, status_id):
103         """delete and tombstone a status"""
104         status = get_object_or_404(
105             models.Status.objects.select_subclasses(), id=status_id
106         )
107         if isinstance(status, (models.GeneratedNote, models.ReviewRating)):
108             return HttpResponseBadRequest()
109 
110         # don't let people redraft other people's statuses
111         if status.user != request.user:
112             return HttpResponseBadRequest()
113 
114         status_type = status.status_type.lower()
115         if status.reply_parent:
116             status_type = "reply"
117 
118         data = {
119             "draft": status,
120             "type": status_type,
121         }
122         if hasattr(status, "book"):
123             data["book"] = status.book
124         elif status.mention_books:
125             data["book"] = status.mention_books.first()
126 
127         # perform deletion
128         status.delete()
129         return TemplateResponse(request, "compose.html", data)
130 
131 
132 def find_mentions(content):
133     """detect @mentions in raw status content"""
134     if not content:
135         return
136     for match in re.finditer(regex.STRICT_USERNAME, content):
137         username = match.group().strip().split("@")[1:]
138         if len(username) == 1:
139             # this looks like a local user (@user), fill in the domain
140             username.append(DOMAIN)
141         username = "@".join(username)
142 
143         mention_user = handle_remote_webfinger(username)
144         if not mention_user:
145             # we can ignore users we don't know about
146             continue
147         yield (match.group(), mention_user)
148 
149 
150 def format_links(content):
151     """detect and format links"""
152     return re.sub(
153         r'([^(href=")]|^|\()(https?:\/\/(%s([\w\.\-_\/+&\?=:;,])*))' % regex.DOMAIN,
154         r'\g<1><a href="\g<2>">\g<3></a>',
155         content,
156     )
157 
158 
159 def to_markdown(content):
160     """catch links and convert to markdown"""
161     content = markdown(content)
162     content = format_links(content)
163     # sanitize resulting html
164     sanitizer = InputHtmlParser()
165     sanitizer.feed(content)
166     return sanitizer.get_output()
167
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/bookwyrm/views/status.py b/bookwyrm/views/status.py
--- a/bookwyrm/views/status.py
+++ b/bookwyrm/views/status.py
@@ -150,7 +150,7 @@
 def format_links(content):
     """detect and format links"""
     return re.sub(
-        r'([^(href=")]|^|\()(https?:\/\/(%s([\w\.\-_\/+&\?=:;,])*))' % regex.DOMAIN,
+        r'([^(href=")]|^|\()(https?:\/\/(%s([\w\.\-_\/+&\?=:;,@#])*))' % regex.DOMAIN,
         r'\g<1><a href="\g<2>">\g<3></a>',
         content,
     )

{"golden_diff": "diff --git a/bookwyrm/views/status.py b/bookwyrm/views/status.py\n--- a/bookwyrm/views/status.py\n+++ b/bookwyrm/views/status.py\n@@ -150,7 +150,7 @@\n def format_links(content):\n     \"\"\"detect and format links\"\"\"\n     return re.sub(\n-        r'([^(href=\")]|^|\\()(https?:\\/\\/(%s([\\w\\.\\-_\\/+&\\?=:;,])*))' % regex.DOMAIN,\n+        r'([^(href=\")]|^|\\()(https?:\\/\\/(%s([\\w\\.\\-_\\/+&\\?=:;,@#])*))' % regex.DOMAIN,\n         r'\\g<1><a href=\"\\g<2>\">\\g<3></a>',\n         content,\n     )\n", "issue": "full links not recognized\n**Describe the bug**\r\nOnly the first part of a link with a /@ in it is being linked so https://mastodon.social/@username only auto links https://mastodon.social and strips '@username' from being a link\r\n\r\n**To Reproduce**\r\nhere is an example https://bookwyrm.social/user/wakest/comment/30867\r\n\r\n**Expected behavior**\r\nWhen a link is in a comment, the whole link should be turned into a link not just part of it\r\n\r\n**Screenshots**\r\n<img width=\"858\" alt=\"image\" src=\"https://user-images.githubusercontent.com/7890201/124171841-ae45cd80-da6e-11eb-9071-a74596df184a.png\">\r\n\r\n**Instance**\r\nhttps://bookwyrm.social\n", "before_files": [{"content": "\"\"\" what are we here for if not for posting \"\"\"\nimport re\nfrom django.contrib.auth.decorators import login_required\nfrom django.http import HttpResponseBadRequest\nfrom django.shortcuts import get_object_or_404, redirect\nfrom django.template.response import TemplateResponse\nfrom django.utils.decorators import method_decorator\nfrom django.views import View\nfrom markdown import markdown\n\nfrom bookwyrm import forms, models\nfrom bookwyrm.sanitize_html import InputHtmlParser\nfrom bookwyrm.settings import DOMAIN\nfrom bookwyrm.utils import regex\nfrom .helpers import handle_remote_webfinger\nfrom .reading import edit_readthrough\n\n\n# pylint: disable= no-self-use\n@method_decorator(login_required, name=\"dispatch\")\nclass CreateStatus(View):\n    \"\"\"the view for *posting*\"\"\"\n\n    def get(self, request, status_type):  # pylint: disable=unused-argument\n        \"\"\"compose view (used for delete-and-redraft\"\"\"\n        book = get_object_or_404(models.Edition, id=request.GET.get(\"book\"))\n        data = {\"book\": book}\n        return TemplateResponse(request, \"compose.html\", data)\n\n    def post(self, request, status_type):\n        \"\"\"create  status of whatever type\"\"\"\n        status_type = status_type[0].upper() + status_type[1:]\n\n        try:\n            form = getattr(forms, \"%sForm\" % status_type)(request.POST)\n        except AttributeError:\n            return HttpResponseBadRequest()\n        if not form.is_valid():\n            return redirect(request.headers.get(\"Referer\", \"/\"))\n\n        status = form.save(commit=False)\n        if not status.sensitive and status.content_warning:\n            # the cw text field remains populated when you click \"remove\"\n            status.content_warning = None\n        status.save(broadcast=False)\n\n        # inspect the text for user tags\n        content = status.content\n        for (mention_text, mention_user) in find_mentions(content):\n            # add them to status mentions fk\n            status.mention_users.add(mention_user)\n\n            # turn the mention into a link\n            content = re.sub(\n                r\"%s([^@]|$)\" % mention_text,\n                r'<a href=\"%s\">%s</a>\\g<1>' % (mention_user.remote_id, mention_text),\n                content,\n            )\n        # add reply parent to mentions\n        if status.reply_parent:\n            status.mention_users.add(status.reply_parent.user)\n\n        # deduplicate mentions\n        status.mention_users.set(set(status.mention_users.all()))\n\n        # don't apply formatting to generated notes\n        if not isinstance(status, models.GeneratedNote) and content:\n            status.content = to_markdown(content)\n        # do apply formatting to quotes\n        if hasattr(status, \"quote\"):\n            status.quote = to_markdown(status.quote)\n\n        status.save(created=True)\n\n        # update a readthorugh, if needed\n        edit_readthrough(request)\n\n        return redirect(\"/\")\n\n\n@method_decorator(login_required, name=\"dispatch\")\nclass DeleteStatus(View):\n    \"\"\"tombstone that bad boy\"\"\"\n\n    def post(self, request, status_id):\n        \"\"\"delete and tombstone a status\"\"\"\n        status = get_object_or_404(models.Status, id=status_id)\n\n        # don't let people delete other people's statuses\n        if status.user != request.user and not request.user.has_perm(\"moderate_post\"):\n            return HttpResponseBadRequest()\n\n        # perform deletion\n        status.delete()\n        return redirect(request.headers.get(\"Referer\", \"/\"))\n\n\n@method_decorator(login_required, name=\"dispatch\")\nclass DeleteAndRedraft(View):\n    \"\"\"delete a status but let the user re-create it\"\"\"\n\n    def post(self, request, status_id):\n        \"\"\"delete and tombstone a status\"\"\"\n        status = get_object_or_404(\n            models.Status.objects.select_subclasses(), id=status_id\n        )\n        if isinstance(status, (models.GeneratedNote, models.ReviewRating)):\n            return HttpResponseBadRequest()\n\n        # don't let people redraft other people's statuses\n        if status.user != request.user:\n            return HttpResponseBadRequest()\n\n        status_type = status.status_type.lower()\n        if status.reply_parent:\n            status_type = \"reply\"\n\n        data = {\n            \"draft\": status,\n            \"type\": status_type,\n        }\n        if hasattr(status, \"book\"):\n            data[\"book\"] = status.book\n        elif status.mention_books:\n            data[\"book\"] = status.mention_books.first()\n\n        # perform deletion\n        status.delete()\n        return TemplateResponse(request, \"compose.html\", data)\n\n\ndef find_mentions(content):\n    \"\"\"detect @mentions in raw status content\"\"\"\n    if not content:\n        return\n    for match in re.finditer(regex.STRICT_USERNAME, content):\n        username = match.group().strip().split(\"@\")[1:]\n        if len(username) == 1:\n            # this looks like a local user (@user), fill in the domain\n            username.append(DOMAIN)\n        username = \"@\".join(username)\n\n        mention_user = handle_remote_webfinger(username)\n        if not mention_user:\n            # we can ignore users we don't know about\n            continue\n        yield (match.group(), mention_user)\n\n\ndef format_links(content):\n    \"\"\"detect and format links\"\"\"\n    return re.sub(\n        r'([^(href=\")]|^|\\()(https?:\\/\\/(%s([\\w\\.\\-_\\/+&\\?=:;,])*))' % regex.DOMAIN,\n        r'\\g<1><a href=\"\\g<2>\">\\g<3></a>',\n        content,\n    )\n\n\ndef to_markdown(content):\n    \"\"\"catch links and convert to markdown\"\"\"\n    content = markdown(content)\n    content = format_links(content)\n    # sanitize resulting html\n    sanitizer = InputHtmlParser()\n    sanitizer.feed(content)\n    return sanitizer.get_output()\n", "path": "bookwyrm/views/status.py"}], "after_files": [{"content": "\"\"\" what are we here for if not for posting \"\"\"\nimport re\nfrom django.contrib.auth.decorators import login_required\nfrom django.http import HttpResponseBadRequest\nfrom django.shortcuts import get_object_or_404, redirect\nfrom django.template.response import TemplateResponse\nfrom django.utils.decorators import method_decorator\nfrom django.views import View\nfrom markdown import markdown\n\nfrom bookwyrm import forms, models\nfrom bookwyrm.sanitize_html import InputHtmlParser\nfrom bookwyrm.settings import DOMAIN\nfrom bookwyrm.utils import regex\nfrom .helpers import handle_remote_webfinger\nfrom .reading import edit_readthrough\n\n\n# pylint: disable= no-self-use\n@method_decorator(login_required, name=\"dispatch\")\nclass CreateStatus(View):\n    \"\"\"the view for *posting*\"\"\"\n\n    def get(self, request, status_type):  # pylint: disable=unused-argument\n        \"\"\"compose view (used for delete-and-redraft\"\"\"\n        book = get_object_or_404(models.Edition, id=request.GET.get(\"book\"))\n        data = {\"book\": book}\n        return TemplateResponse(request, \"compose.html\", data)\n\n    def post(self, request, status_type):\n        \"\"\"create  status of whatever type\"\"\"\n        status_type = status_type[0].upper() + status_type[1:]\n\n        try:\n            form = getattr(forms, \"%sForm\" % status_type)(request.POST)\n        except AttributeError:\n            return HttpResponseBadRequest()\n        if not form.is_valid():\n            return redirect(request.headers.get(\"Referer\", \"/\"))\n\n        status = form.save(commit=False)\n        if not status.sensitive and status.content_warning:\n            # the cw text field remains populated when you click \"remove\"\n            status.content_warning = None\n        status.save(broadcast=False)\n\n        # inspect the text for user tags\n        content = status.content\n        for (mention_text, mention_user) in find_mentions(content):\n            # add them to status mentions fk\n            status.mention_users.add(mention_user)\n\n            # turn the mention into a link\n            content = re.sub(\n                r\"%s([^@]|$)\" % mention_text,\n                r'<a href=\"%s\">%s</a>\\g<1>' % (mention_user.remote_id, mention_text),\n                content,\n            )\n        # add reply parent to mentions\n        if status.reply_parent:\n            status.mention_users.add(status.reply_parent.user)\n\n        # deduplicate mentions\n        status.mention_users.set(set(status.mention_users.all()))\n\n        # don't apply formatting to generated notes\n        if not isinstance(status, models.GeneratedNote) and content:\n            status.content = to_markdown(content)\n        # do apply formatting to quotes\n        if hasattr(status, \"quote\"):\n            status.quote = to_markdown(status.quote)\n\n        status.save(created=True)\n\n        # update a readthorugh, if needed\n        edit_readthrough(request)\n\n        return redirect(\"/\")\n\n\n@method_decorator(login_required, name=\"dispatch\")\nclass DeleteStatus(View):\n    \"\"\"tombstone that bad boy\"\"\"\n\n    def post(self, request, status_id):\n        \"\"\"delete and tombstone a status\"\"\"\n        status = get_object_or_404(models.Status, id=status_id)\n\n        # don't let people delete other people's statuses\n        if status.user != request.user and not request.user.has_perm(\"moderate_post\"):\n            return HttpResponseBadRequest()\n\n        # perform deletion\n        status.delete()\n        return redirect(request.headers.get(\"Referer\", \"/\"))\n\n\n@method_decorator(login_required, name=\"dispatch\")\nclass DeleteAndRedraft(View):\n    \"\"\"delete a status but let the user re-create it\"\"\"\n\n    def post(self, request, status_id):\n        \"\"\"delete and tombstone a status\"\"\"\n        status = get_object_or_404(\n            models.Status.objects.select_subclasses(), id=status_id\n        )\n        if isinstance(status, (models.GeneratedNote, models.ReviewRating)):\n            return HttpResponseBadRequest()\n\n        # don't let people redraft other people's statuses\n        if status.user != request.user:\n            return HttpResponseBadRequest()\n\n        status_type = status.status_type.lower()\n        if status.reply_parent:\n            status_type = \"reply\"\n\n        data = {\n            \"draft\": status,\n            \"type\": status_type,\n        }\n        if hasattr(status, \"book\"):\n            data[\"book\"] = status.book\n        elif status.mention_books:\n            data[\"book\"] = status.mention_books.first()\n\n        # perform deletion\n        status.delete()\n        return TemplateResponse(request, \"compose.html\", data)\n\n\ndef find_mentions(content):\n    \"\"\"detect @mentions in raw status content\"\"\"\n    if not content:\n        return\n    for match in re.finditer(regex.STRICT_USERNAME, content):\n        username = match.group().strip().split(\"@\")[1:]\n        if len(username) == 1:\n            # this looks like a local user (@user), fill in the domain\n            username.append(DOMAIN)\n        username = \"@\".join(username)\n\n        mention_user = handle_remote_webfinger(username)\n        if not mention_user:\n            # we can ignore users we don't know about\n            continue\n        yield (match.group(), mention_user)\n\n\ndef format_links(content):\n    \"\"\"detect and format links\"\"\"\n    return re.sub(\n        r'([^(href=\")]|^|\\()(https?:\\/\\/(%s([\\w\\.\\-_\\/+&\\?=:;,@#])*))' % regex.DOMAIN,\n        r'\\g<1><a href=\"\\g<2>\">\\g<3></a>',\n        content,\n    )\n\n\ndef to_markdown(content):\n    \"\"\"catch links and convert to markdown\"\"\"\n    content = markdown(content)\n    content = format_links(content)\n    # sanitize resulting html\n    sanitizer = InputHtmlParser()\n    sanitizer.feed(content)\n    return sanitizer.get_output()\n", "path": "bookwyrm/views/status.py"}]}