Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
linkify.py150 linesDownload Raw Back to rules_core
1from __future__ import annotations2 3import re4from typing import Protocol5 6from ..common.utils import arrayReplaceAt, isLinkClose, isLinkOpen7from ..token import Token8from .state_core import StateCore9 10HTTP_RE = re.compile(r"^http://")11MAILTO_RE = re.compile(r"^mailto:")12TEST_MAILTO_RE = re.compile(r"^mailto:", flags=re.IGNORECASE)13 14 15def linkify(state: StateCore) -> None:16    """Rule for identifying plain-text links."""17    if not state.md.options.linkify:18        return19 20    if not state.md.linkify:21        raise ModuleNotFoundError("Linkify enabled but not installed.")22 23    for inline_token in state.tokens:24        if inline_token.type != "inline" or not state.md.linkify.pretest(25            inline_token.content26        ):27            continue28 29        tokens = inline_token.children30 31        htmlLinkLevel = 032 33        # We scan from the end, to keep position when new tags added.34        # Use reversed logic in links start/end match35        assert tokens is not None36        i = len(tokens)37        while i >= 1:38            i -= 139            assert isinstance(tokens, list)40            currentToken = tokens[i]41 42            # Skip content of markdown links43            if currentToken.type == "link_close":44                i -= 145                while (46                    tokens[i].level != currentToken.level47                    and tokens[i].type != "link_open"48                ):49                    i -= 150                continue51 52            # Skip content of html tag links53            if currentToken.type == "html_inline":54                if isLinkOpen(currentToken.content) and htmlLinkLevel > 0:55                    htmlLinkLevel -= 156                if isLinkClose(currentToken.content):57                    htmlLinkLevel += 158            if htmlLinkLevel > 0:59                continue60 61            if currentToken.type == "text" and state.md.linkify.test(62                currentToken.content63            ):64                text = currentToken.content65                links: list[_LinkType] = state.md.linkify.match(text) or []66 67                # Now split string to nodes68                nodes = []69                level = currentToken.level70                lastPos = 071 72                # forbid escape sequence at the start of the string,73                # this avoids http\://example.com/ from being linkified as74                # http:<a href="//example.com/">//example.com/</a>75                if (76                    links77                    and links[0].index == 078                    and i > 079                    and tokens[i - 1].type == "text_special"80                ):81                    links = links[1:]82 83                for link in links:84                    url = link.url85                    fullUrl = state.md.normalizeLink(url)86                    if not state.md.validateLink(fullUrl):87                        continue88 89                    urlText = link.text90 91                    # Linkifier might send raw hostnames like "example.com", where url92                    # starts with domain name. So we prepend http:// in those cases,93                    # and remove it afterwards.94                    if not link.schema:95                        urlText = HTTP_RE.sub(96                            "", state.md.normalizeLinkText("http://" + urlText)97                        )98                    elif link.schema == "mailto:" and TEST_MAILTO_RE.search(urlText):99                        urlText = MAILTO_RE.sub(100                            "", state.md.normalizeLinkText("mailto:" + urlText)101                        )102                    else:103                        urlText = state.md.normalizeLinkText(urlText)104 105                    pos = link.index106 107                    if pos > lastPos:108                        token = Token("text", "", 0)109                        token.content = text[lastPos:pos]110                        token.level = level111                        nodes.append(token)112 113                    token = Token("link_open", "a", 1)114                    token.attrs = {"href": fullUrl}115                    token.level = level116                    level += 1117                    token.markup = "linkify"118                    token.info = "auto"119                    nodes.append(token)120 121                    token = Token("text", "", 0)122                    token.content = urlText123                    token.level = level124                    nodes.append(token)125 126                    token = Token("link_close", "a", -1)127                    level -= 1128                    token.level = level129                    token.markup = "linkify"130                    token.info = "auto"131                    nodes.append(token)132 133                    lastPos = link.last_index134 135                if lastPos < len(text):136                    token = Token("text", "", 0)137                    token.content = text[lastPos:]138                    token.level = level139                    nodes.append(token)140 141                inline_token.children = tokens = arrayReplaceAt(tokens, i, nodes)142 143 144class _LinkType(Protocol):145    url: str146    text: str147    index: int148    last_index: int149    schema: str | None150 
codekingpro/portable-devtools · Team Ai