1
1
"""A custom Transform object to shorten github and gitlab links."""
2
2
3
- from typing import ClassVar
3
+ from typing import ClassVar , Literal
4
4
from urllib .parse import ParseResult , urlparse , urlunparse
5
5
6
6
from docutils import nodes
7
7
from sphinx .transforms .post_transforms import SphinxPostTransform
8
8
from sphinx .util .nodes import NodeMatcher
9
9
10
- from .utils import traverse_or_findall
10
+ from .utils import get_theme_options_dict , traverse_or_findall
11
11
12
12
13
13
class ShortenLinkTransform (SphinxPostTransform ):
14
14
"""
15
- Shorten link when they are coming from github or gitlab and add an extra class to
16
- the tag for further styling.
15
+ Shorten links leading to supported forges.
16
+ Also attempt to identify self-hosted forge instances.
17
+ Add an extra class to the tag for further styling.
17
18
18
19
Before:
19
20
.. code-block:: html
@@ -35,6 +36,8 @@ class ShortenLinkTransform(SphinxPostTransform):
35
36
default_priority = 400
36
37
formats = ("html" ,)
37
38
supported_platform : ClassVar [dict [str , str ]] = {
39
+ "codeberg.org" : "codeberg" ,
40
+ "gitea.com" : "gitea" ,
38
41
"github.com" : "github" ,
39
42
"gitlab.com" : "gitlab" ,
40
43
}
@@ -53,10 +56,54 @@ def run(self, **kwargs):
53
56
uri = urlparse (uri )
54
57
# only do something if the platform is identified
55
58
self .platform = self .supported_platform .get (uri .netloc )
59
+ # or we can make a reasonable guess about self-hosted forges
60
+ if self .platform is None :
61
+ html_theme_options = get_theme_options_dict (self .app )
62
+ self .platform = self .identify_selfhosted (uri , html_theme_options )
56
63
if self .platform is not None :
57
64
node .attributes ["classes" ].append (self .platform )
58
65
node .children [0 ] = nodes .Text (self .parse_url (uri ))
59
66
67
+ def identify_selfhosted (
68
+ self , uri : ParseResult , html_theme_options : dict [str , str ]
69
+ ) -> Literal ["forgejo" , "gitea" , "gitlab" ] | None :
70
+ """Try to identify what self-hosted forge uri leads to (if any).
71
+
72
+ Args:
73
+ uri: the link to the platform content
74
+ html_theme_options: varia
75
+
76
+ Returns:
77
+ likely platform if one matches, None otherwise
78
+ """
79
+ # forge name in authority and known url part in the right place
80
+ # unreliable but may catch any number of hosts
81
+ path_parts = uri .path .strip ("/" ).split ("/" )
82
+ if len (path_parts ) > 2 and path_parts [2 ] in ("pulls" , "issues" , "projects" ):
83
+ if "forgejo" in uri .netloc :
84
+ return "forgejo"
85
+ elif "gitea" in uri .netloc :
86
+ return "gitea"
87
+ if (
88
+ len (path_parts ) > 3
89
+ and path_parts [2 ] == "-"
90
+ and path_parts [3 ] in ("issues" , "merge_requests" )
91
+ ):
92
+ if "gitlab" in uri .netloc :
93
+ return "gitlab"
94
+
95
+ # url passed in *_url option
96
+ # will only match project's own forge but that's
97
+ # likely where most doc links will lead anyway
98
+ str_url = f"{ uri .scheme } ://{ uri .netloc } "
99
+ selfhosted = ("forgejo" , "gitea" , "gitlab" )
100
+ for forge in selfhosted :
101
+ known_url = html_theme_options .get (f"{ forge } _url" )
102
+ if known_url and known_url .startswith (str_url ):
103
+ return forge
104
+
105
+ return None
106
+
60
107
def parse_url (self , uri : ParseResult ) -> str :
61
108
"""Parse the content of the url with respect to the selected platform.
62
109
@@ -119,5 +166,14 @@ def parse_url(self, uri: ParseResult) -> str:
119
166
# for example "<group>/<subgroup1>/<subgroup2>/<repository>"
120
167
text = uri ._replace (netloc = "" , scheme = "" ) # remove platform
121
168
text = urlunparse (text )[1 :] # combine to string and strip leading "/"
169
+ elif self .platform in ("codeberg" , "forgejo" , "gitea" ):
170
+ parts = path .rstrip ("/" ).split ("/" )
171
+ if len (parts ) == 4 and parts [2 ] in ("issues" , "pulls" ):
172
+ text = f"{ parts [0 ]} /{ parts [1 ]} #{ parts [3 ]} " # element number
173
+ elif parts == ["" ]:
174
+ text = self .platform
175
+ else :
176
+ text = uri ._replace (netloc = "" , scheme = "" ) # remove platform
177
+ text = urlunparse (text )[1 :] # combine to string and strip leading "/"
122
178
123
179
return text
0 commit comments