Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/source/components/analyse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Limitations

**Current Limitations:**

- **Language Support**: C/C++ (``//``, ``/* */``), C# (``//``, ``/* */``, ``///``), Python (``#``), YAML (``#``) and Rust (``//``, ``/* */``, ``///``) comment styles are supported
- **Language Support**: C/C++ (``//``, ``/* */``), C# (``//``, ``/* */``, ``///``), Python (``#``), YAML (``#``), Rust (``//``, ``/* */``, ``///``) and Go (``//``, ``/* */``) comment styles are supported
- **Single Comment Style**: Each analysis run processes only one comment style at a time

Extraction Examples
Expand Down
7 changes: 6 additions & 1 deletion docs/source/components/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ Specifies the comment syntax style used in the source code files. This determine

**Type:** ``str``
**Default:** ``"cpp"``
**Supported values:** ``"cpp"``, ``"python"``, ``"cs"``, ``"yaml"``, ``"rust"``
**Supported values:** ``"cpp"``, ``"python"``, ``"cs"``, ``"yaml"``, ``"rust"``, ``"go"``

.. code-block:: toml

Expand Down Expand Up @@ -315,6 +315,11 @@ Specifies the comment syntax style used in the source code files. This determine
``///`` (doc comments),
``//!`` (inner doc comments)
- ``.rs``
* - Go
- ``"go"``
- ``//`` (single-line),
``/* */`` (multi-line)
- ``.go``

.. note:: Future versions may support additional programming languages.

Expand Down
25 changes: 25 additions & 0 deletions docs/source/components/features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,31 @@ Features
.. fault:: Sphinx-codelinks halucinates traceability objects in Rust
:id: FAULT_RUST_2

.. feature:: Go Language Support
:id: FE_GO

Support for defining traceability objects in Go source files via one-line comment annotations.

The Go language parser leverages tree-sitter to accurately identify and extract
comments from Go source files, including single-line (``//``) and multi-line
(``/* */``) comment styles. This ensures that traceability markers are correctly
associated with the appropriate code structures such as functions, methods, types,
and structs.

Key capabilities:

* Detection of inline and block comments
* Association of comments with function and method declarations
* Support for standard Go comment conventions
* Accurate scope detection for nested structures
* File extension ``.go`` auto-discovered when ``comment_type = "go"``

.. fault:: Traceability objects are not detected in Go language
:id: FAULT_GO_1

.. fault:: Sphinx-codelinks halucinates traceability objects in Go
:id: FAULT_GO_2

.. feature:: Customized comment styles
:id: FE_CMT

Expand Down
4 changes: 4 additions & 0 deletions docs/source/development/change_log.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ Upcoming
--------

- ⬆️ Support and test sphinx-needs v5-8
- ✨ Added Go parser for the ``analyse`` module.

Need ID references and one-line need definitions can now be extracted from Go source files.
The supported comment styles are ``//`` and ``/* */``.

.. _`release:1.2.0`:

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies = [
"tree-sitter-c-sharp>=0.23.1",
"tree-sitter-yaml>=0.7.1",
"tree-sitter-rust>=0.23.0",
"tree-sitter-go>=0.23.0",
]

[build-system]
Expand Down
18 changes: 17 additions & 1 deletion src/sphinx_codelinks/analyse/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@
"trait_item",
"mod_item",
},
# @Go Scope Node Types, IMPL_GO_4, impl, [FE_GO]
CommentType.go: {
"function_declaration",
"method_declaration",
"type_declaration",
"type_spec",
},
}

# initialize logger
Expand Down Expand Up @@ -60,6 +67,10 @@
(line_comment) @comment
(block_comment) @comment
"""
# @Go comment query for tree-sitter, IMPL_GO_3, impl, [FE_GO]
GO_QUERY = """
(comment) @comment
"""


def is_text_file(filepath: Path, sample_size: int = 2048) -> bool:
Expand All @@ -77,7 +88,7 @@ def is_text_file(filepath: Path, sample_size: int = 2048) -> bool:
return False


# @Tree-sitter parser initialization for multiple languages, IMPL_LANG_1, impl, [FE_C_SUPPORT, FE_CPP, FE_PY, FE_YAML, FE_RUST]
# @Tree-sitter parser initialization for multiple languages, IMPL_LANG_1, impl, [FE_C_SUPPORT, FE_CPP, FE_PY, FE_YAML, FE_RUST, FE_GO]
def init_tree_sitter(comment_type: CommentType) -> tuple[Parser, Query]:
if comment_type == CommentType.cpp:
import tree_sitter_cpp # noqa: PLC0415
Expand All @@ -104,6 +115,11 @@ def init_tree_sitter(comment_type: CommentType) -> tuple[Parser, Query]:

parsed_language = Language(tree_sitter_rust.language())
query = Query(parsed_language, RUST_QUERY)
elif comment_type == CommentType.go:
import tree_sitter_go # noqa: PLC0415

parsed_language = Language(tree_sitter_go.language())
query = Query(parsed_language, GO_QUERY)
else:
raise ValueError(f"Unsupported comment style: {comment_type}")
parser = Parser(parsed_language)
Expand Down
2 changes: 2 additions & 0 deletions src/sphinx_codelinks/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
# @Support Python style comments, IMPL_PY_1, impl, [FE_PY]
CommentType.python: ["#"],
CommentType.cs: ["//", "/*", "///"],
# @Support Go style comments, IMPL_GO_2, impl, [FE_GO]
CommentType.go: ["//", "/*"],
}
ESCAPE = "\\"

Expand Down
3 changes: 3 additions & 0 deletions src/sphinx_codelinks/source_discover/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"cs": ["cs"],
"yaml": ["yml", "yaml"],
"rust": ["rs"],
"go": ["go"],
}


Expand All @@ -21,6 +22,8 @@ class CommentType(str, Enum):
yaml = "yaml"
# @Support Rust style comments, IMPL_RUST_1, impl, [FE_RUST];
rust = "rust"
# @Support Go style comments, IMPL_GO_1, impl, [FE_GO];
go = "go"


class SourceDiscoverSectionConfigType(TypedDict, total=False):
Expand Down
Comment thread
arnoox marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<document source="<source>">
<target anonymous="" ids="IMPL_1" refid="IMPL_1">
<Need classes="need need-impl" ids="IMPL_1" refid="IMPL_1">
27 changes: 27 additions & 0 deletions tests/doc_test/go_basic/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

project = "source-tracing-demo"
copyright = "2025, useblocks"
author = "useblocks"

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

extensions = ["sphinx_needs", "sphinx_codelinks"]

templates_path = ["_templates"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]

src_trace_config_from_toml = "src_trace.toml"

# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output

html_theme = "alabaster"
html_static_path = ["_static"]
8 changes: 8 additions & 0 deletions tests/doc_test/go_basic/dummy_src.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package main

import "fmt"

// @ title here, IMPL_1, impl
func singleLineExample() {
fmt.Println("Single-line comment example")
}
2 changes: 2 additions & 0 deletions tests/doc_test/go_basic/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.. src-trace::
:project: src
5 changes: 5 additions & 0 deletions tests/doc_test/go_basic/src_trace.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[codelinks.projects.src]
remote_url_pattern = "https://github.com/useblocks/sphinx-codelinks/blob/{commit}/{path}#L{line}"

[codelinks.projects.src.source_discover]
comment_type = "go"
169 changes: 169 additions & 0 deletions tests/test_analyse_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from tree_sitter import Node as TreeSitterNode
import tree_sitter_c_sharp
import tree_sitter_cpp
import tree_sitter_go
import tree_sitter_python
import tree_sitter_rust
import tree_sitter_yaml
Expand Down Expand Up @@ -57,6 +58,14 @@ def init_rust_tree_sitter() -> tuple[Parser, Query]:
return parser, query


@pytest.fixture(scope="session")
def init_go_tree_sitter() -> tuple[Parser, Query]:
parsed_language = Language(tree_sitter_go.language())
query = Query(parsed_language, utils.GO_QUERY)
parser = Parser(parsed_language)
return parser, query


@pytest.mark.parametrize(
("code", "result"),
[
Expand Down Expand Up @@ -815,6 +824,166 @@ def test_yaml_comment(code, num_comments, result, init_yaml_tree_sitter):
assert comments[0].text.decode("utf-8") == result


@pytest.mark.parametrize(
("code", "num_comments", "result"),
[
(
b"""
// @req-id: need_001
func dummyFunc1() {
}
""",
1,
"// @req-id: need_001",
),
(
b"""
func dummyFunc1() {
// @req-id: need_001
}
""",
1,
"// @req-id: need_001",
),
(
b"""
/* @req-id: need_001 */
func dummyFunc1() {
}
""",
1,
"/* @req-id: need_001 */",
),
(
b"""
// @req-id: need_001
//
//
func dummyFunc1() {
}
""",
3,
"// @req-id: need_001",
),
],
)
def test_go_comment(code, num_comments, result, init_go_tree_sitter):
parser, query = init_go_tree_sitter
comments: list[TreeSitterNode] = utils.extract_comments(code, parser, query)
comments.sort(key=lambda x: x.start_point.row)
assert len(comments) == num_comments
assert comments[0].text
assert comments[0].text.decode("utf-8") == result


@pytest.mark.parametrize(
("code", "result"),
[
(
b"""
// @req-id: need_001
func dummyFunc1() {
}
""",
"func dummyFunc1()",
),
(
b"""
func dummyFunc2() {
}
// @req-id: need_001
func dummyFunc1() {
}
""",
"func dummyFunc1()",
),
(
b"""
/* @req-id: need_001 */
func dummyFunc1() {
}
""",
"func dummyFunc1()",
),
],
)
def test_find_associated_scope_go(code, result, init_go_tree_sitter):
parser, query = init_go_tree_sitter
comments = utils.extract_comments(code, parser, query)
node: TreeSitterNode | None = utils.find_associated_scope(
comments[0], CommentType.go
)
assert node
assert node.text
go_def = node.text.decode("utf-8")
assert result in go_def


@pytest.mark.parametrize(
("code", "result"),
[
(
b"""
// @req-id: need_001
func dummyFunc1() {
}
""",
"func dummyFunc1()",
),
(
b"""
// @req-id: need_001
type DummyStruct struct {
Field int
}
""",
"type DummyStruct struct",
),
],
)
def test_find_next_scope_go(code, result, init_go_tree_sitter):
parser, query = init_go_tree_sitter
comments = utils.extract_comments(code, parser, query)
node: TreeSitterNode | None = utils.find_next_scope(comments[0], CommentType.go)
assert node
assert node.text
go_def = node.text.decode("utf-8")
assert result in go_def


@pytest.mark.parametrize(
("code", "result"),
[
(
b"""
func dummyFunc1() {
// @req-id: need_001
}
""",
"func dummyFunc1()",
),
(
b"""
func dummyFunc1() {
/* @req-id: need_001 */
}
""",
"func dummyFunc1()",
),
],
)
def test_find_enclosing_scope_go(code, result, init_go_tree_sitter):
parser, query = init_go_tree_sitter
comments = utils.extract_comments(code, parser, query)
node: TreeSitterNode | None = utils.find_enclosing_scope(
comments[0], CommentType.go
)
assert node
assert node.text
go_def = node.text.decode("utf-8")
assert result in go_def


@pytest.mark.parametrize(
("git_url", "rev", "project_path", "filepath", "lineno", "result"),
[
Expand Down
Loading
Loading