Repository: PyCQA/docformatter (python)

Issue:
Bugs in link handling in 1.5.1
I've run into a couple issues related to formatting links in 1.5.1.

1. Error with short link

To reproduce, create a file `tmp.py` containing:
```
"""
Test test.

See `the link <https://www.link.com>`_ for more details.
"""
```
Running `docformatter tmp.py` gives an error
```
   ...
  File ".../lib/python3.9/site-packages/docformatter/syntax.py", line 272, in wrap_description
    text = do_preserve_links(text, indentation, wrap_length)
  File ".../lib/python3.9/site-packages/docformatter/syntax.py", line 82, in do_preserve_links
    url = url + lines[url_idx + 1].strip()
IndexError: list index out of range
```

2. Long links that get broken across lines also remove spaces from the text following the link

```
"""
Test test.

See `the link <https://www.link.com/a/long/link/that/causes/line/break>`_ for more details.
"""
```
Running `docformatter tmp.py` produces the output
```
--- before/tmp.py
+++ after/tmp.py
@@ -1,5 +1,6 @@
+"""Test test.
+
+See `the link
+
+<https://www.link.com/a/long/link/that/causes/line/break>`_ for moredetails.
 """
-Test test.
-
-See `the link <https://www.link.com/a/long/link/that/causes/line/break>`_ for more details.
-"""
```
Note that the space has been removed between "more" and "details".

Pull request description:
fix: handle index error in link wrapping
This pull request:

* Skips link handling when there is nothing wrapped in the original text.
* Maintains wraps when the link is short enough to be contained on a single line along with additional words.

Closes #140 

Patch:
diff --git a/src/docformatter/syntax.py b/src/docformatter/syntax.py
index 0ef5aca..127ea2b 100644
--- a/src/docformatter/syntax.py
+++ b/src/docformatter/syntax.py
@@ -62,6 +62,10 @@ def do_preserve_links(
         subsequent_indent=indentation,
     )
 
+    # There is nothing to do if the input wasn't wrapped.
+    if len(lines) < 2:
+        return lines
+
     url = next(
         (
             line
@@ -77,16 +81,23 @@ def do_preserve_links(
         # Is this an in-line link (i.e., enclosed in <>)?  We want to keep
         # the '<' and '>' part of the link.
         if re.search(r"<", url):
-            lines[url_idx] = f"{indentation}" + url.split(sep="<")[0].strip()
+            if len(url.split(sep="<")[0].strip()) > 0:
+                lines[url_idx] = (
+                    f"{indentation}" + url.split(sep="<")[0].strip()
+                )
+
             url = f"{indentation}<" + url.split(sep="<")[1]
-            url = url + lines[url_idx + 1].strip()
-            lines[url_idx + 1] = url
+            if len(url.split(sep=">")) < 2:
+                url = url + lines[url_idx + 1].strip()
+                lines[url_idx + 1] = url
+
         # Is this a link target definition (i.e., .. a link: https://)?  We
         # want to keep the .. a link: on the same line as the url.
         elif re.search(r"(\.\. )", url):
             url = url + lines[url_idx + 1].strip()
             lines[url_idx] = url
             lines.pop(url_idx + 1)
+
         # Is this a simple link (i.e., just a link in the text) that should
         # be unwrapped?  We want to break the url out from the rest of the
         # text.


Test patch:
diff --git a/tests/test_format_docstring.py b/tests/test_format_docstring.py
index 8e8dae6..99c34e4 100644
--- a/tests/test_format_docstring.py
+++ b/tests/test_format_docstring.py
@@ -797,6 +797,82 @@ num_iterations is the number of updates - instead of a better definition of conv
             INDENTATION, docstring.strip()
         )
 
+    @pytest.mark.unit
+    @pytest.mark.parametrize(
+        "args",
+        [["--wrap-descriptions", "72", ""]],
+    )
+    def test_format_docstring_with_short_inline_link(
+            self,
+            test_args,
+            args,
+    ):
+        """Short in-line links will remain untouched.
+
+        See issue #140. See requirement docformatter_10.1.3.1.
+        """
+        uut = Formatter(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
+        docstring = '''\
+"""This is yanf with a short link.
+
+    See `the link <https://www.link.com`_ for more details.
+    """\
+'''
+
+        assert '''\
+"""This is yanf with a short link.
+
+    See `the link <https://www.link.com`_ for more details.
+    """\
+''' == uut._do_format_docstring(
+            INDENTATION, docstring.strip()
+        )
+
+    @pytest.mark.unit
+    @pytest.mark.parametrize(
+            "args",
+            [["--wrap-descriptions", "72", ""]],
+        )
+    def test_format_docstring_with_inline_link_retain_spaces(
+                self,
+                test_args,
+                args,
+    ):
+        """In-line links shouldn't remove blank spaces between words.
+
+        See issue #140.
+        """
+        uut = Formatter(
+                test_args,
+                sys.stderr,
+                sys.stdin,
+                sys.stdout,
+        )
+
+        docstring = '''\
+"""This is a docstring with a link that causes a wrap.
+
+    See `the link <https://www.link.com/a/long/link/that/causes/line/break>`_ for more details.
+    """\
+'''
+
+        assert '''\
+"""This is a docstring with a link that causes a wrap.
+
+    See `the link
+    <https://www.link.com/a/long/link/that/causes/line/break>`_ for more
+    details.
+    """\
+''' == uut._do_format_docstring(
+                INDENTATION, docstring.strip()
+            )
+
     @pytest.mark.unit
     @pytest.mark.parametrize(
         "args",