Repository: tsutsu3/linkify-it-py

Issue:
Support for linkify-it update (Fix parenthesis after commas)
https://github.com/markdown-it/linkify-it/commit/eb7463ddb02e748c04578fa017519ce6abf15841

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c4e0d49..2cb51fd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Change Log
 
+## vx.x.x (2022-xx-xx) / Draft
+
+- Add `matchAtStart` method to match full URLs at the start of the string.
+
 ## v1.0.3 (2021-12-18)
 
 - Fixed [#98](https://github.com/markdown-it/linkify-it/issues/98). Don't count `;` at the end of link (when followed with space).
diff --git a/README.md b/README.md
index bd45d54..439c647 100644
--- a/README.md
+++ b/README.md
@@ -185,6 +185,11 @@ Each match has:
 - __text__ - normalized text
 - __url__ - link, generated from matched text
 
+### .matchAtStart(text)
+
+Checks if a match exists at the start of the string. Returns `Match`
+(see docs for `match(text)`) or null if no URL is at the start.
+Doesn't work with fuzzy links.
 
 ### .tlds(list_tlds, keep_old=False)
 
diff --git a/linkify_it/main.py b/linkify_it/main.py
index 8d9254b..6aa8903 100644
--- a/linkify_it/main.py
+++ b/linkify_it/main.py
@@ -361,6 +361,7 @@ class LinkifyIt:
         # (?!_) cause 1.5x slowdown
         self.re["schema_test"] = re_schema_test
         self.re["schema_search"] = re_schema_test
+        self.re["schema_at_start"] = "^" + self.re["schema_search"]
 
         self.re["pretest"] = (
             "(" + re_schema_test + ")|(" + self.re["host_fuzzy_test"] + ")|@"
@@ -559,6 +560,38 @@ class LinkifyIt:
 
         return None
 
+    def match_at_start(self, text):
+        """Returns fully-formed (not fuzzy) link if it starts at the beginning
+        of the string, and null otherwise.
+
+        Args:
+            text (str): text to search
+
+        Retuns:
+            ``Match`` or ``None``
+        """
+        # Reset scan cache
+        self._text_cache = text
+        self._index = -1
+
+        if not len(text):
+            return None
+
+        founds = re.search(self.re["schema_at_start"], text, flags=re.IGNORECASE)
+        if not founds:
+            return None
+
+        m = (founds.group(), founds.groups()[0], founds.groups()[1])
+        length = self.test_schema_at(text, m[2], len(m[0]))
+        if not length:
+            return None
+
+        self._schema = m[2]
+        self._index = founds.start(0) + len(m[1])
+        self._last_index = founds.start(0) + len(m[0]) + length
+
+        return self._create_match(0)
+
     def tlds(self, list_tlds, keep_old=False):
         """Load (or merge) new tlds list. (chainable)
 
diff --git a/linkify_it/ucre.py b/linkify_it/ucre.py
index 46176e4..52f25ee 100644
--- a/linkify_it/ucre.py
+++ b/linkify_it/ucre.py
@@ -171,7 +171,7 @@ def _re_src_path(opts):
         + "|[']).)+\\'|"
         + "\\'(?="
         + SRC_PSEUDO_LETTER
-        + "|[-]).|"
+        + "|[-])|"
         + "\\.{2,}[a-zA-Z0-9%/&]|"
         # google has many dots in "google search" links (#66, #81).
         # github has ... in commit range links,
@@ -183,20 +183,20 @@ def _re_src_path(opts):
         # until more examples found.
         + "\\.(?!"
         + SRC_ZCC
-        + "|[.]).|"
+        + "|[.]|$)|"
         + options
         + ",(?!"
         + SRC_ZCC
-        + ").|"  # allow `,,,` in paths
+        + "|$)|"  # allow `,,,` in paths
         + ";(?!"
         + SRC_ZCC
-        + ").|"  # allow `,,,` in paths
+        + "|$)|"  # allow `,,,` in paths
         + "\\!+(?!"
         + SRC_ZCC
-        + "|[!]).|"  # allow `!!!` in paths, but not at the end
+        + "|[!]|$)|"  # allow `!!!` in paths, but not at the end
         + "\\?(?!"
         + SRC_ZCC
-        + "|[?])."
+        + "|[?]|$)"
         + ")+"
         + "|\\/"
         + ")?"


Test patch:
diff --git a/test/fixtures/links.txt b/test/fixtures/links.txt
index d624a04..29b2a9b 100644
--- a/test/fixtures/links.txt
+++ b/test/fixtures/links.txt
@@ -162,6 +162,8 @@ http://example.com/foo_bar/
 http://example.com/foo_bar,
 http://example.com/foo_bar
 
+http://index-of.es/Android/Professional.Android.2.Application.Development.(Wrox,.2010,.0470565527).pdf
+
 https://github.com/markdown-it/linkify-it/compare/360b13a733f521a8d4903d3a5e1e46c357e9d3ce...f580766349525150a80a32987bb47c2d592efc33
 
 https://www.google.com/search?sxsrf=ACYBGNTJFmX-GjNJ8fM-2LCkqyNyxGU1Ng%3A1575534146332&ei=Qr7oXf7rE4rRrgSEgrmoAw&q=clover&oq=clover&gs_l=psy-ab.3..0i67j0l9.2986.3947..4187...0.2..0.281.1366.1j0j5......0....1..gws-wiz.......0i71j35i39j0i131.qWp1nz4IJVA&ved=0ahUKEwj-lP6Iip7mAhWKqIsKHQRBDjUQ4dUDCAs&uact=5
diff --git a/test/test_apis.py b/test/test_apis.py
index 093f293..20c71db 100644
--- a/test/test_apis.py
+++ b/test/test_apis.py
@@ -280,3 +280,40 @@ def test_api_add_alias_rule_with_excape_re_string(escape_str):
 
     linkifyit.add("foo{}bar:".format(escape_str), "http:")
     assert linkifyit.test("Check foo{}bar://test".format(escape_str)) is True
+
+
+def test_api_blank_test_match_at_the_start():
+    linkifyit = LinkifyIt()
+
+    assert not linkifyit.match_at_start("")
+
+
+def test_api_should_find_a_match_at_the_start():
+    linkifyit = LinkifyIt()
+
+    linkifyit = LinkifyIt(options={"fuzzy_link": True})
+    linkifyit.set({"fuzzy_link": True})
+
+    assert not linkifyit.test("@@invalid")
+    assert not linkifyit.match_at_start("google.com 123")
+    assert not linkifyit.match_at_start("  http://google.com 123")
+
+
+def test_api_match_a_start_should_not_interfere_with_normal_match():
+    linkifyit = LinkifyIt()
+
+    str = "http://google.com http://google.com"
+    assert linkifyit.match_at_start(str)
+    assert len(linkifyit.match(str)) == 2
+
+    str = "aaa http://google.com http://google.com"
+    assert not linkifyit.match_at_start(str)
+    assert len(linkifyit.match(str)) == 2
+
+
+def test_api_should_not_match_incomplete_links():
+    # regression test for https://github.com/markdown-it/markdown-it/issues/868
+    linkifyit = LinkifyIt()
+
+    assert not linkifyit.match_at_start("http://")
+    assert not linkifyit.match_at_start("https://")