sphinx-codelinks 0.1.1__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/PKG-INFO +1 -1
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/pyproject.toml +1 -1
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/cmd.py +8 -8
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/source_discovery/config.py +6 -4
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/source_discovery/source_discover.py +2 -2
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/sphinx_extension/config.py +37 -20
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/sphinx_extension/directives/src_trace.py +44 -23
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/sphinx_extension/source_tracing.py +7 -0
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/LICENSE +0 -0
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/README.md +0 -0
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/README.md +0 -0
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/__init__.py +0 -0
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/sphinx_extension/debug.py +0 -0
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/sphinx_extension/html_wrapper.py +0 -0
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/sphinx_extension/ub_sct.css +0 -0
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/virtual_docs/config.py +0 -0
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/virtual_docs/ubt_models.py +0 -0
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/virtual_docs/utils.py +0 -0
- {sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/virtual_docs/virtual_docs.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sphinx-codelinks
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Fast Source Code Traceability for Sphinx-Needs
|
|
5
5
|
Author-email: team useblocks <info@useblocks.com>
|
|
6
6
|
Maintainer-email: Marco Heinemann <marco.heinemann@useblocks.com>, Jui-Wen Chen <jui-wen.chen@useblocks.com>
|
|
@@ -26,7 +26,7 @@ app = typer.Typer(
|
|
|
26
26
|
|
|
27
27
|
@app.command(no_args_is_help=True)
|
|
28
28
|
def discover(
|
|
29
|
-
|
|
29
|
+
src_dir: Annotated[
|
|
30
30
|
Path,
|
|
31
31
|
typer.Argument(
|
|
32
32
|
...,
|
|
@@ -68,7 +68,7 @@ def discover(
|
|
|
68
68
|
from sphinx_codelinks.source_discovery.source_discover import SourceDiscover
|
|
69
69
|
|
|
70
70
|
source_discover = SourceDiscover(
|
|
71
|
-
|
|
71
|
+
src_dir=src_dir,
|
|
72
72
|
exclude=exclude,
|
|
73
73
|
include=include,
|
|
74
74
|
file_types=file_types,
|
|
@@ -134,8 +134,8 @@ def vdoc(
|
|
|
134
134
|
if oneline_errors:
|
|
135
135
|
errors.appendleft("Invalid oneline comment style configuration:")
|
|
136
136
|
errors.extend(oneline_errors)
|
|
137
|
-
|
|
138
|
-
src_discovery_dict
|
|
137
|
+
src_discovery_errors = []
|
|
138
|
+
src_discovery_dict = build_src_discovery_dict(project_config)
|
|
139
139
|
if src_discovery_dict:
|
|
140
140
|
src_discovery_config = SourceDiscoveryConfig(**src_discovery_dict)
|
|
141
141
|
else:
|
|
@@ -151,9 +151,9 @@ def vdoc(
|
|
|
151
151
|
|
|
152
152
|
from sphinx_codelinks.source_discovery.source_discover import SourceDiscover
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
src_dir = (config.parent / src_discovery_config.src_dir).resolve()
|
|
155
155
|
source_discover = SourceDiscover(
|
|
156
|
-
|
|
156
|
+
src_dir=src_dir,
|
|
157
157
|
exclude=src_discovery_config.exclude,
|
|
158
158
|
include=src_discovery_config.include,
|
|
159
159
|
file_types=src_discovery_config.file_types,
|
|
@@ -164,7 +164,7 @@ def vdoc(
|
|
|
164
164
|
|
|
165
165
|
virtual_docs = VirtualDocs(
|
|
166
166
|
src_files=source_discover.source_paths,
|
|
167
|
-
src_dir=str(
|
|
167
|
+
src_dir=str(src_dir),
|
|
168
168
|
output_dir=str(output_dir),
|
|
169
169
|
oneline_comment_style=oneline_comment_style,
|
|
170
170
|
comment_type=src_discovery_config.file_types[0]
|
|
@@ -178,7 +178,7 @@ def vdoc(
|
|
|
178
178
|
typer.echo("The virtual documents are generated:")
|
|
179
179
|
for v_doc in virtual_docs.virtual_docs:
|
|
180
180
|
json_path = output_dir / v_doc.filepath.with_suffix(".json").relative_to(
|
|
181
|
-
|
|
181
|
+
src_dir
|
|
182
182
|
)
|
|
183
183
|
typer.echo(json_path)
|
|
184
184
|
else:
|
{sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/source_discovery/config.py
RENAMED
|
@@ -6,7 +6,7 @@ from jsonschema import ValidationError, validate
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class SourceDiscoveryConfigType(TypedDict, total=False):
|
|
9
|
-
|
|
9
|
+
src_dir: Path
|
|
10
10
|
exclude: list[str]
|
|
11
11
|
include: list[str]
|
|
12
12
|
gitignore: bool
|
|
@@ -19,8 +19,8 @@ class SourceDiscoveryConfig:
|
|
|
19
19
|
def field_names(cls) -> set[str]:
|
|
20
20
|
return {item.name for item in fields(cls)}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
default_factory=lambda: Path
|
|
22
|
+
src_dir: Path = field(
|
|
23
|
+
default_factory=lambda: Path("./"), metadata={"schema": {"type": "string"}}
|
|
24
24
|
)
|
|
25
25
|
"""The root of the source directory."""
|
|
26
26
|
|
|
@@ -41,7 +41,9 @@ class SourceDiscoveryConfig:
|
|
|
41
41
|
|
|
42
42
|
file_types: list[str] = field(
|
|
43
43
|
default_factory=lambda: ["c", "h", "cpp", "hpp"],
|
|
44
|
-
metadata={
|
|
44
|
+
metadata={
|
|
45
|
+
"schema": {"type": "array", "items": {"enum": ["c", "h", "cpp", "hpp"]}}
|
|
46
|
+
},
|
|
45
47
|
)
|
|
46
48
|
"""The file types to discover."""
|
|
47
49
|
|
|
@@ -9,13 +9,13 @@ from gitignore_parser import parse_gitignore # type: ignore[import-untyped]
|
|
|
9
9
|
class SourceDiscover:
|
|
10
10
|
def __init__(
|
|
11
11
|
self,
|
|
12
|
-
|
|
12
|
+
src_dir: Path,
|
|
13
13
|
exclude: list[str] | None = None,
|
|
14
14
|
include: list[str] | None = None,
|
|
15
15
|
gitignore: bool = True,
|
|
16
16
|
file_types: list[str] | None = None,
|
|
17
17
|
):
|
|
18
|
-
self.root_path =
|
|
18
|
+
self.root_path = src_dir
|
|
19
19
|
self.exclude = exclude
|
|
20
20
|
self.include = include
|
|
21
21
|
# Only gitignore at source root is considered.
|
{sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/sphinx_extension/config.py
RENAMED
|
@@ -43,7 +43,7 @@ class SrcTraceProjectConfigFileType(TypedDict):
|
|
|
43
43
|
class SrcTraceProjectConfigType(TypedDict):
|
|
44
44
|
# only support C/C++ for now
|
|
45
45
|
comment_type: Literal["cpp", "hpp", "c", "h"]
|
|
46
|
-
src_dir:
|
|
46
|
+
src_dir: Path
|
|
47
47
|
remote_url_pattern: str
|
|
48
48
|
exclude: list[str]
|
|
49
49
|
include: list[str]
|
|
@@ -241,9 +241,8 @@ def check_project_configuration(config: SrcTraceSphinxConfig) -> list[str]:
|
|
|
241
241
|
for project_name, project_config in config.projects.items():
|
|
242
242
|
project_errors: list[str] = []
|
|
243
243
|
oneline_errors = validate_oneline_comment_style(project_config)
|
|
244
|
-
src_discovery_dict
|
|
245
|
-
|
|
246
|
-
)
|
|
244
|
+
src_discovery_dict = build_src_discovery_dict(project_config)
|
|
245
|
+
src_discovery_errors = []
|
|
247
246
|
if src_discovery_dict is not None:
|
|
248
247
|
src_discovery_config = SourceDiscoveryConfig(**src_discovery_dict)
|
|
249
248
|
src_discovery_errors.extend(src_discovery_config.check_schema())
|
|
@@ -286,22 +285,40 @@ def validate_oneline_comment_style(
|
|
|
286
285
|
|
|
287
286
|
def build_src_discovery_dict(
|
|
288
287
|
project_config: SrcTraceProjectConfigType,
|
|
289
|
-
) ->
|
|
288
|
+
) -> SourceDiscoveryConfigType | None:
|
|
290
289
|
src_discovery_dict = cast(SourceDiscoveryConfigType, {})
|
|
291
|
-
|
|
292
|
-
if "
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
290
|
+
# adapt the configs between source tracing and source discovery
|
|
291
|
+
if "comment_type" in project_config:
|
|
292
|
+
# comment type error will be taken care by SourceDiscovery class later
|
|
293
|
+
src_discovery_dict["file_types"] = (
|
|
294
|
+
list(SUPPORTED_COMMENT_TYPES)
|
|
295
|
+
if project_config["comment_type"] in SUPPORTED_COMMENT_TYPES
|
|
296
|
+
else [project_config["comment_type"]]
|
|
297
|
+
)
|
|
298
|
+
for key in ("exclude", "include", "gitignore", "src_dir"):
|
|
298
299
|
if key in project_config:
|
|
299
300
|
src_discovery_dict[key] = project_config[key]
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
301
|
+
|
|
302
|
+
return src_discovery_dict
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def adpat_src_discovery_config(project_config: SrcTraceProjectConfigType) -> None:
|
|
306
|
+
src_discovery_dict = build_src_discovery_dict(project_config)
|
|
307
|
+
if src_discovery_dict:
|
|
308
|
+
src_discovery_config = SourceDiscoveryConfig(**src_discovery_dict)
|
|
309
|
+
else:
|
|
310
|
+
src_discovery_config = SourceDiscoveryConfig()
|
|
311
|
+
|
|
312
|
+
for _field in fields(src_discovery_config):
|
|
313
|
+
key = "comment_type" if _field.name == "file_types" else _field.name
|
|
314
|
+
|
|
315
|
+
if key == "comment_type":
|
|
316
|
+
file_types = getattr(src_discovery_config, _field.name)
|
|
317
|
+
if set(file_types) == SUPPORTED_COMMENT_TYPES:
|
|
318
|
+
comment_type = "cpp"
|
|
319
|
+
else:
|
|
320
|
+
comment_type = file_types[0]
|
|
321
|
+
project_config[key] = comment_type # type: ignore[literal-required] # dynamically assign
|
|
322
|
+
continue
|
|
323
|
+
|
|
324
|
+
project_config[key] = getattr(src_discovery_config, _field.name) # type: ignore[literal-required] # dynamically assign
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
|
+
import os
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
import subprocess
|
|
4
5
|
from typing import Any, ClassVar, cast
|
|
@@ -35,13 +36,23 @@ logger = logging.getLogger(__name__)
|
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
def generate_str_link_name(
|
|
38
|
-
comment: UBTComment,
|
|
39
|
+
comment: UBTComment,
|
|
40
|
+
target_filepath: Path,
|
|
41
|
+
dirs: dict[str, Path],
|
|
42
|
+
local: bool = False,
|
|
39
43
|
) -> str:
|
|
40
44
|
if comment.start_line == comment.end_line:
|
|
41
45
|
lineno = f"L{comment.start_line}"
|
|
42
46
|
else:
|
|
43
47
|
lineno = f"L{comment.start_line}-L{comment.end_line}"
|
|
44
|
-
url = str(target_filepath.relative_to(target_dir)) + f"#{lineno}"
|
|
48
|
+
# url = str(target_filepath.relative_to(target_dir)) + f"#{lineno}"
|
|
49
|
+
if local:
|
|
50
|
+
url = str(target_filepath) + f"#{lineno}"
|
|
51
|
+
else:
|
|
52
|
+
remote_path = dirs["remote_src_dir"] / target_filepath.relative_to(
|
|
53
|
+
dirs["target_dir"]
|
|
54
|
+
)
|
|
55
|
+
url = f"{remote_path!s}#{lineno}"
|
|
45
56
|
|
|
46
57
|
return url
|
|
47
58
|
|
|
@@ -151,6 +162,12 @@ class SourceTracingDirective(SphinxDirective):
|
|
|
151
162
|
)
|
|
152
163
|
needs.extend(src_trace_need)
|
|
153
164
|
|
|
165
|
+
dirs = {
|
|
166
|
+
"src_dir": src_dir,
|
|
167
|
+
"out_dir": out_dir,
|
|
168
|
+
"target_dir": target_dir,
|
|
169
|
+
}
|
|
170
|
+
|
|
154
171
|
# inject needs_string_links config before add_need()
|
|
155
172
|
# https://sphinx-needs.readthedocs.io/en/latest/configuration.html#needs-string-links
|
|
156
173
|
# local URL
|
|
@@ -158,12 +175,13 @@ class SourceTracingDirective(SphinxDirective):
|
|
|
158
175
|
remote_url_field = None
|
|
159
176
|
if src_trace_sphinx_config.set_local_url:
|
|
160
177
|
local_url_field = src_trace_sphinx_config.local_url_field
|
|
178
|
+
to_remove_str = f"{out_dir!s}{os.sep}"
|
|
179
|
+
if os.name == "nt":
|
|
180
|
+
to_remove_str = to_remove_str.replace("\\", "\\\\")
|
|
161
181
|
self.env.config.needs_string_links[local_url_field] = {
|
|
162
182
|
"regex": r"^(?P<value>.+?)\.[^\.]+#L(?P<lineno>\d+)",
|
|
163
|
-
"link_url": (
|
|
164
|
-
|
|
165
|
-
),
|
|
166
|
-
"link_name": "{{value}}#L{{lineno}}",
|
|
183
|
+
"link_url": ("file://{{value}}.html#L-{{lineno}}"),
|
|
184
|
+
"link_name": f"{{{{value | replace('{to_remove_str}', '')}}}}#L{{{{lineno}}}}",
|
|
167
185
|
"options": [local_url_field],
|
|
168
186
|
}
|
|
169
187
|
if (
|
|
@@ -178,9 +196,11 @@ class SourceTracingDirective(SphinxDirective):
|
|
|
178
196
|
remote_src_dir = src_dir
|
|
179
197
|
else:
|
|
180
198
|
remote_src_dir = src_dir.relative_to(git_root_path)
|
|
199
|
+
dirs["remote_src_dir"] = remote_src_dir
|
|
181
200
|
remote_url_pattern = src_trace_conf["remote_url_pattern"].format(
|
|
182
201
|
commit=commit_id,
|
|
183
|
-
path=f"{remote_src_dir}/" + "{{value}}",
|
|
202
|
+
# path=f"{remote_src_dir}/" + "{{value}}",
|
|
203
|
+
path="{{value}}",
|
|
184
204
|
line="{{lineno}}",
|
|
185
205
|
)
|
|
186
206
|
self.env.config.needs_string_links[remote_url_field] = {
|
|
@@ -189,11 +209,7 @@ class SourceTracingDirective(SphinxDirective):
|
|
|
189
209
|
"link_name": "{{value}}#L{{lineno}}",
|
|
190
210
|
"options": [remote_url_field],
|
|
191
211
|
}
|
|
192
|
-
|
|
193
|
-
"src_dir": src_dir,
|
|
194
|
-
"out_dir": out_dir,
|
|
195
|
-
"target_dir": target_dir,
|
|
196
|
-
}
|
|
212
|
+
|
|
197
213
|
# render needs from the source files
|
|
198
214
|
rendered_needs = self.render_needs(
|
|
199
215
|
virtual_docs,
|
|
@@ -287,13 +303,18 @@ class SourceTracingDirective(SphinxDirective):
|
|
|
287
303
|
target_filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
288
304
|
target_filepath.write_text(filepath.read_text())
|
|
289
305
|
for comment in virtual_doc.comments:
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
if local_url_field
|
|
306
|
+
local_link_name = None
|
|
307
|
+
remote_link_name = None
|
|
308
|
+
if local_url_field:
|
|
293
309
|
# generate link name
|
|
294
|
-
|
|
295
|
-
comment, target_filepath,
|
|
310
|
+
local_link_name = generate_str_link_name(
|
|
311
|
+
comment, target_filepath, dirs, local=True
|
|
296
312
|
)
|
|
313
|
+
if remote_url_field:
|
|
314
|
+
remote_link_name = generate_str_link_name(
|
|
315
|
+
comment, target_filepath, dirs, local=False
|
|
316
|
+
)
|
|
317
|
+
|
|
297
318
|
if comment.resolved_marker:
|
|
298
319
|
# render needs from one-line marker
|
|
299
320
|
kwargs: dict[str, str | list[str]] = {
|
|
@@ -306,12 +327,12 @@ class SourceTracingDirective(SphinxDirective):
|
|
|
306
327
|
] # title and type are mandatory for add_need()
|
|
307
328
|
}
|
|
308
329
|
|
|
309
|
-
if local_url_field and
|
|
310
|
-
kwargs[local_url_field] =
|
|
311
|
-
if remote_url_field and
|
|
312
|
-
kwargs[remote_url_field] =
|
|
330
|
+
if local_url_field and local_link_name is not None:
|
|
331
|
+
kwargs[local_url_field] = local_link_name
|
|
332
|
+
if remote_url_field and remote_link_name is not None:
|
|
333
|
+
kwargs[remote_url_field] = remote_link_name
|
|
313
334
|
|
|
314
|
-
|
|
335
|
+
oneline_needs: list[nodes.Node] = add_need(
|
|
315
336
|
app=self.env.app, # The Sphinx application object
|
|
316
337
|
state=self.state, # The docutils state object
|
|
317
338
|
docname=self.env.docname, # The current document name
|
|
@@ -324,7 +345,7 @@ class SourceTracingDirective(SphinxDirective):
|
|
|
324
345
|
), # The title of the need
|
|
325
346
|
**cast(dict[str, Any], kwargs), # type: ignore[explicit-any]
|
|
326
347
|
)
|
|
327
|
-
rendered_needs.extend(
|
|
348
|
+
rendered_needs.extend(oneline_needs)
|
|
328
349
|
if local_url_field:
|
|
329
350
|
# save the mapping of need links and line numbers of source codes
|
|
330
351
|
# for the later use in `html-collect-pages`
|
|
@@ -21,6 +21,7 @@ from sphinx_codelinks.sphinx_extension.config import (
|
|
|
21
21
|
SrcTraceConfigType,
|
|
22
22
|
SrcTraceProjectConfigType,
|
|
23
23
|
SrcTraceSphinxConfig,
|
|
24
|
+
adpat_src_discovery_config,
|
|
24
25
|
check_configuration,
|
|
25
26
|
file_lineno_href,
|
|
26
27
|
)
|
|
@@ -155,6 +156,10 @@ def set_config_to_sphinx(
|
|
|
155
156
|
for project_config in cast(
|
|
156
157
|
dict[str, SrcTraceProjectConfigType], value
|
|
157
158
|
).values():
|
|
159
|
+
# address SourceDiscovery related config
|
|
160
|
+
adpat_src_discovery_config(project_config)
|
|
161
|
+
|
|
162
|
+
# address OneLoneCommenyStyle config and its default
|
|
158
163
|
oneline_comment_style: OneLineCommentStyleType | None = cast(
|
|
159
164
|
OneLineCommentStyleType, project_config.get("oneline_comment_style")
|
|
160
165
|
)
|
|
@@ -165,6 +170,8 @@ def set_config_to_sphinx(
|
|
|
165
170
|
project_config["oneline_comment_style"],
|
|
166
171
|
)
|
|
167
172
|
)
|
|
173
|
+
else:
|
|
174
|
+
project_config["oneline_comment_style"] = OneLineCommentStyle()
|
|
168
175
|
|
|
169
176
|
config[f"src_trace_{key}"] = value
|
|
170
177
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/sphinx_extension/debug.py
RENAMED
|
File without changes
|
|
File without changes
|
{sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/sphinx_extension/ub_sct.css
RENAMED
|
File without changes
|
{sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/virtual_docs/config.py
RENAMED
|
File without changes
|
{sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/virtual_docs/ubt_models.py
RENAMED
|
File without changes
|
{sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/virtual_docs/utils.py
RENAMED
|
File without changes
|
{sphinx_codelinks-0.1.1 → sphinx_codelinks-0.1.2}/src/sphinx_codelinks/virtual_docs/virtual_docs.py
RENAMED
|
File without changes
|