cod8a 0.1.0__tar.gz → 0.2.0__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.
- {cod8a-0.1.0 → cod8a-0.2.0}/PKG-INFO +69 -52
- {cod8a-0.1.0 → cod8a-0.2.0}/README.md +65 -50
- {cod8a-0.1.0 → cod8a-0.2.0}/pyproject.toml +9 -5
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/cli.py +21 -4
- {cod8a-0.1.0/src/cod8a/dotnet/CodeAnalysis → cod8a-0.2.0/src/cod8a/dotnet/CodeAnalyzer}/Parsers/FileParser.cs +38 -15
- {cod8a-0.1.0/src/cod8a/dotnet/CodeAnalysis → cod8a-0.2.0/src/cod8a/dotnet/CodeAnalyzer}/Parsers/ProjectParser.cs +10 -4
- {cod8a-0.1.0/src/cod8a/dotnet/CodeAnalysis → cod8a-0.2.0/src/cod8a/dotnet/CodeAnalyzer}/Parsers/SolutionParser.cs +1 -1
- {cod8a-0.1.0/src/cod8a/dotnet/CodeAnalysis → cod8a-0.2.0/src/cod8a/dotnet/CodeAnalyzer}/Program.cs +18 -15
- {cod8a-0.1.0/src/cod8a/dotnet/CodeAnalysis → cod8a-0.2.0/src/cod8a/dotnet/CodeAnalyzer}/models/FileStructure.cs +3 -3
- {cod8a-0.1.0/src/cod8a/dotnet/CodeAnalysis → cod8a-0.2.0/src/cod8a/dotnet/CodeAnalyzer}/models/ProjectStructure.cs +3 -3
- {cod8a-0.1.0/src/cod8a/dotnet/CodeAnalysis → cod8a-0.2.0/src/cod8a/dotnet/CodeAnalyzer}/models/SolutionStructure.cs +3 -3
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/dotnet/Test/CodeAnalyzerTest.csproj +1 -1
- cod8a-0.2.0/src/cod8a/dotnet/Test/ExtensionTypeTest.cs +64 -0
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/generators/mermaid/class_diagram.py +11 -5
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/generators/mermaid/flowchart_diagram.py +18 -11
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/generators/mermaid/sequence_diagram.py +10 -8
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/helpers/cli_helper.py +2 -2
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/parsers/dotnet_parser.py +10 -2
- {cod8a-0.1.0 → cod8a-0.2.0}/LICENSE +0 -0
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/__init__.py +0 -0
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/cod8a.py +0 -0
- {cod8a-0.1.0/src/cod8a/dotnet/CodeAnalysis → cod8a-0.2.0/src/cod8a/dotnet/CodeAnalyzer}/CodeAnalyzer.csproj +0 -0
- {cod8a-0.1.0/src/cod8a/dotnet/CodeAnalysis → cod8a-0.2.0/src/cod8a/dotnet/CodeAnalyzer}/CodeAnalyzer.csproj.lscache +0 -0
- {cod8a-0.1.0/src/cod8a/dotnet/CodeAnalysis → cod8a-0.2.0/src/cod8a/dotnet/CodeAnalyzer}/Parsers/BaseParser.cs +0 -0
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/dotnet/Test/Mermaid/ClassDiagramTest.cs +0 -0
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/enums/__init__.py +0 -0
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/enums/diagram_type.py +0 -0
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/helpers/__init__.py +0 -0
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/models/__init__.py +0 -0
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/models/models.py +0 -0
- {cod8a-0.1.0 → cod8a-0.2.0}/src/cod8a/parsers/python_parser.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cod8a
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: cod8a is a code documentation and visualization tool for Python and .NET projects
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Keywords: visualization,uml,docs,flowchart,class,sequence,diagram
|
|
8
|
-
Author: Akumbom
|
|
8
|
+
Author: Marietta Akumbom
|
|
9
9
|
Author-email: akumbom5ma@gmail.com
|
|
10
10
|
Maintainer: Marietta Akumbom
|
|
11
11
|
Requires-Python: >=3.13
|
|
@@ -14,6 +14,8 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.14
|
|
15
15
|
Requires-Dist: click (>=8.3.3,<9.0.0)
|
|
16
16
|
Requires-Dist: pydantic (>=2.13.3,<3.0.0)
|
|
17
|
+
Project-URL: Homepage, https://github.com/marietta-a/cod8a
|
|
18
|
+
Project-URL: Repository, https://github.com/marietta-a/cod8a
|
|
17
19
|
Description-Content-Type: text/markdown
|
|
18
20
|
|
|
19
21
|
# cod8a
|
|
@@ -27,61 +29,17 @@ cod8a pronounced codetta, is a code documentation and visualization tool for **P
|
|
|
27
29
|
- **[Python 3.9+](https://www.python.org/downloads/)**
|
|
28
30
|
- **[.NET 10.0 Runtime or SDK](https://dotnet.microsoft.com/download/dotnet/10.0)** (Required **only** for C#/.NET analysis)
|
|
29
31
|
|
|
30
|
-
### From PyPI
|
|
32
|
+
### From PyPI
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
Install `cod8a` directly using pip:
|
|
33
35
|
|
|
34
36
|
```bash
|
|
35
37
|
pip install cod8a
|
|
36
38
|
```
|
|
37
39
|
|
|
38
|
-
## Development & Contribution
|
|
39
|
-
|
|
40
|
-
If you want to contribute to the project or run it from source:
|
|
41
|
-
|
|
42
|
-
### Setup
|
|
43
|
-
|
|
44
|
-
1. **Fork** the repository on GitHub.
|
|
45
|
-
2. **Clone** your fork:
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
git clone https://github.com/yourusername/cod8a.git
|
|
49
|
-
cd cod8a
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Development Options
|
|
53
|
-
|
|
54
|
-
#### 1. Local Setup
|
|
55
|
-
3. **Install** Python dependencies using **[Poetry](https://python-poetry.org/docs/#installation)**:
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
poetry install
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
4. **Optional**: Ensure .NET 10.0 is installed (if documenting C#)
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
dotnet --version
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
#### 2. VS Code Dev Container (Recommended)
|
|
68
|
-
If you use VS Code, you can skip the local setup by using the provided **Dev Container**. It comes pre-configured with Python 3.13, .NET 10.0, and Poetry.
|
|
69
|
-
- Simply open the project in VS Code and click **"Reopen in Container"** when prompted.
|
|
70
|
-
|
|
71
|
-
#### 3. Docker
|
|
72
|
-
You can also run `cod8a` as a standalone container without installing any dependencies locally:
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
# Build the image
|
|
76
|
-
docker build -t cod8a .
|
|
77
|
-
|
|
78
|
-
# Run the tool
|
|
79
|
-
docker run -v $(pwd):/app/data cod8a uml -p /app/data/src
|
|
80
|
-
```
|
|
81
|
-
|
|
82
40
|
## Usage
|
|
83
41
|
|
|
84
|
-
The tool uses a CLI interface to analyze code and generate
|
|
42
|
+
The tool uses a CLI interface to analyze code and generate mermaid diagrams.
|
|
85
43
|
|
|
86
44
|
### UML Diagrams
|
|
87
45
|
|
|
@@ -92,13 +50,19 @@ Generate Mermaid-compatible diagrams (Class, Sequence, Flowchart):
|
|
|
92
50
|
cod8a uml -p <path_to_source> -d <diagram_type>
|
|
93
51
|
|
|
94
52
|
# Generate a class diagram (default)
|
|
95
|
-
cod8a uml -p
|
|
53
|
+
cod8a uml -p <path_to_source>
|
|
96
54
|
|
|
97
55
|
# Generate a sequence diagram
|
|
98
|
-
cod8a uml -p
|
|
56
|
+
cod8a uml -p <path_to_source> -d sequence
|
|
99
57
|
|
|
100
58
|
# Generate a flowchart
|
|
101
|
-
cod8a uml -p
|
|
59
|
+
cod8a uml -p <path_to_source> -d flow
|
|
60
|
+
|
|
61
|
+
# Summarize a large project (omit fields/methods)
|
|
62
|
+
cod8a uml -p <path_to_source> --summarize
|
|
63
|
+
|
|
64
|
+
# Force full diagram details for a large file
|
|
65
|
+
cod8a uml -p <path_to_source> --no-summarize
|
|
102
66
|
```
|
|
103
67
|
|
|
104
68
|
#### Diagram Type Options:
|
|
@@ -106,11 +70,19 @@ cod8a uml -p src/my_logic -d flow
|
|
|
106
70
|
- **Sequence:** `sequence`, `seq`, `s`
|
|
107
71
|
- **Flowchart:** `flowchart`, `flow`, `f`
|
|
108
72
|
|
|
73
|
+
### Samples
|
|
74
|
+
Below are detailed example Mermaid diagrams generated by `cod8a` based on its own `src/cod8a/models/models.py` file:
|
|
75
|
+
|
|
76
|
+
- [Class Diagram Example](examples/class_diagram.mmd)
|
|
77
|
+
- [Flowchart Example](examples/flowchart.mmd)
|
|
78
|
+
- [Sequence Diagram Example](examples/sequence_diagram.mmd)
|
|
79
|
+
|
|
109
80
|
### CLI Options
|
|
110
81
|
|
|
111
82
|
- `-p, --path`: Path to the file or directory to analyze.
|
|
112
83
|
- `-d, --diagram`: Type of diagram to generate (default: `class`).
|
|
113
84
|
- `-o, --output`: Output file path (saves as `.mmd`).
|
|
85
|
+
- `-s, --summarize / --no-summarize`: Enable or disable diagram summarization. When enabled, details like fields and methods are omitted (useful for large files). Defaults to auto-summarize for large files.
|
|
114
86
|
|
|
115
87
|
### Documentation (In development)
|
|
116
88
|
|
|
@@ -132,3 +104,48 @@ cod8a doc -p path/to/source
|
|
|
132
104
|
- **Markdown Documentation (TODO):** Detailed markdown generation for code documentation is currently in development.
|
|
133
105
|
|
|
134
106
|
|
|
107
|
+
<!-- ## Development & Contribution -->
|
|
108
|
+
<!-- If you want to contribute to the project or run it from source: -->
|
|
109
|
+
## Development
|
|
110
|
+
|
|
111
|
+
### Setup
|
|
112
|
+
|
|
113
|
+
1. **Fork** the repository on GitHub.
|
|
114
|
+
2. **Clone** your fork:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
git clone https://github.com/yourusername/cod8a.git
|
|
118
|
+
cd cod8a
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Development Options
|
|
122
|
+
|
|
123
|
+
#### 1. Local Setup
|
|
124
|
+
3. **Install** Python dependencies:
|
|
125
|
+
|
|
126
|
+
Using **[Poetry](https://python-poetry.org/docs/#installation)** (recommended):
|
|
127
|
+
```bash
|
|
128
|
+
poetry install
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
4. **Optional**: Ensure .NET 10.0 is installed (if documenting C#)
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
dotnet --version
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### 2. VS Code Dev Container (Recommended)
|
|
138
|
+
If you use VS Code, you can skip the local setup by using the provided **Dev Container**. It comes pre-configured with Python 3.13, .NET 10.0, and Poetry.
|
|
139
|
+
- Simply open the project in VS Code and click **"Reopen in Container"** when prompted.
|
|
140
|
+
|
|
141
|
+
#### 3. Docker
|
|
142
|
+
You can also run `cod8a` as a standalone container without installing any dependencies locally:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Build the image
|
|
146
|
+
docker build -t cod8a .
|
|
147
|
+
|
|
148
|
+
# Run the tool
|
|
149
|
+
docker run -v $(pwd):/app/data cod8a uml -p /app/data/src
|
|
150
|
+
```
|
|
151
|
+
|
|
@@ -9,61 +9,17 @@ cod8a pronounced codetta, is a code documentation and visualization tool for **P
|
|
|
9
9
|
- **[Python 3.9+](https://www.python.org/downloads/)**
|
|
10
10
|
- **[.NET 10.0 Runtime or SDK](https://dotnet.microsoft.com/download/dotnet/10.0)** (Required **only** for C#/.NET analysis)
|
|
11
11
|
|
|
12
|
-
### From PyPI
|
|
12
|
+
### From PyPI
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Install `cod8a` directly using pip:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
17
|
pip install cod8a
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
## Development & Contribution
|
|
21
|
-
|
|
22
|
-
If you want to contribute to the project or run it from source:
|
|
23
|
-
|
|
24
|
-
### Setup
|
|
25
|
-
|
|
26
|
-
1. **Fork** the repository on GitHub.
|
|
27
|
-
2. **Clone** your fork:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
git clone https://github.com/yourusername/cod8a.git
|
|
31
|
-
cd cod8a
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### Development Options
|
|
35
|
-
|
|
36
|
-
#### 1. Local Setup
|
|
37
|
-
3. **Install** Python dependencies using **[Poetry](https://python-poetry.org/docs/#installation)**:
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
poetry install
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
4. **Optional**: Ensure .NET 10.0 is installed (if documenting C#)
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
dotnet --version
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
#### 2. VS Code Dev Container (Recommended)
|
|
50
|
-
If you use VS Code, you can skip the local setup by using the provided **Dev Container**. It comes pre-configured with Python 3.13, .NET 10.0, and Poetry.
|
|
51
|
-
- Simply open the project in VS Code and click **"Reopen in Container"** when prompted.
|
|
52
|
-
|
|
53
|
-
#### 3. Docker
|
|
54
|
-
You can also run `cod8a` as a standalone container without installing any dependencies locally:
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
# Build the image
|
|
58
|
-
docker build -t cod8a .
|
|
59
|
-
|
|
60
|
-
# Run the tool
|
|
61
|
-
docker run -v $(pwd):/app/data cod8a uml -p /app/data/src
|
|
62
|
-
```
|
|
63
|
-
|
|
64
20
|
## Usage
|
|
65
21
|
|
|
66
|
-
The tool uses a CLI interface to analyze code and generate
|
|
22
|
+
The tool uses a CLI interface to analyze code and generate mermaid diagrams.
|
|
67
23
|
|
|
68
24
|
### UML Diagrams
|
|
69
25
|
|
|
@@ -74,13 +30,19 @@ Generate Mermaid-compatible diagrams (Class, Sequence, Flowchart):
|
|
|
74
30
|
cod8a uml -p <path_to_source> -d <diagram_type>
|
|
75
31
|
|
|
76
32
|
# Generate a class diagram (default)
|
|
77
|
-
cod8a uml -p
|
|
33
|
+
cod8a uml -p <path_to_source>
|
|
78
34
|
|
|
79
35
|
# Generate a sequence diagram
|
|
80
|
-
cod8a uml -p
|
|
36
|
+
cod8a uml -p <path_to_source> -d sequence
|
|
81
37
|
|
|
82
38
|
# Generate a flowchart
|
|
83
|
-
cod8a uml -p
|
|
39
|
+
cod8a uml -p <path_to_source> -d flow
|
|
40
|
+
|
|
41
|
+
# Summarize a large project (omit fields/methods)
|
|
42
|
+
cod8a uml -p <path_to_source> --summarize
|
|
43
|
+
|
|
44
|
+
# Force full diagram details for a large file
|
|
45
|
+
cod8a uml -p <path_to_source> --no-summarize
|
|
84
46
|
```
|
|
85
47
|
|
|
86
48
|
#### Diagram Type Options:
|
|
@@ -88,11 +50,19 @@ cod8a uml -p src/my_logic -d flow
|
|
|
88
50
|
- **Sequence:** `sequence`, `seq`, `s`
|
|
89
51
|
- **Flowchart:** `flowchart`, `flow`, `f`
|
|
90
52
|
|
|
53
|
+
### Samples
|
|
54
|
+
Below are detailed example Mermaid diagrams generated by `cod8a` based on its own `src/cod8a/models/models.py` file:
|
|
55
|
+
|
|
56
|
+
- [Class Diagram Example](examples/class_diagram.mmd)
|
|
57
|
+
- [Flowchart Example](examples/flowchart.mmd)
|
|
58
|
+
- [Sequence Diagram Example](examples/sequence_diagram.mmd)
|
|
59
|
+
|
|
91
60
|
### CLI Options
|
|
92
61
|
|
|
93
62
|
- `-p, --path`: Path to the file or directory to analyze.
|
|
94
63
|
- `-d, --diagram`: Type of diagram to generate (default: `class`).
|
|
95
64
|
- `-o, --output`: Output file path (saves as `.mmd`).
|
|
65
|
+
- `-s, --summarize / --no-summarize`: Enable or disable diagram summarization. When enabled, details like fields and methods are omitted (useful for large files). Defaults to auto-summarize for large files.
|
|
96
66
|
|
|
97
67
|
### Documentation (In development)
|
|
98
68
|
|
|
@@ -113,3 +83,48 @@ cod8a doc -p path/to/source
|
|
|
113
83
|
- **Python Support:** Uses AST-based parsing for Python files.
|
|
114
84
|
- **Markdown Documentation (TODO):** Detailed markdown generation for code documentation is currently in development.
|
|
115
85
|
|
|
86
|
+
|
|
87
|
+
<!-- ## Development & Contribution -->
|
|
88
|
+
<!-- If you want to contribute to the project or run it from source: -->
|
|
89
|
+
## Development
|
|
90
|
+
|
|
91
|
+
### Setup
|
|
92
|
+
|
|
93
|
+
1. **Fork** the repository on GitHub.
|
|
94
|
+
2. **Clone** your fork:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
git clone https://github.com/yourusername/cod8a.git
|
|
98
|
+
cd cod8a
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Development Options
|
|
102
|
+
|
|
103
|
+
#### 1. Local Setup
|
|
104
|
+
3. **Install** Python dependencies:
|
|
105
|
+
|
|
106
|
+
Using **[Poetry](https://python-poetry.org/docs/#installation)** (recommended):
|
|
107
|
+
```bash
|
|
108
|
+
poetry install
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
4. **Optional**: Ensure .NET 10.0 is installed (if documenting C#)
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
dotnet --version
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### 2. VS Code Dev Container (Recommended)
|
|
118
|
+
If you use VS Code, you can skip the local setup by using the provided **Dev Container**. It comes pre-configured with Python 3.13, .NET 10.0, and Poetry.
|
|
119
|
+
- Simply open the project in VS Code and click **"Reopen in Container"** when prompted.
|
|
120
|
+
|
|
121
|
+
#### 3. Docker
|
|
122
|
+
You can also run `cod8a` as a standalone container without installing any dependencies locally:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Build the image
|
|
126
|
+
docker build -t cod8a .
|
|
127
|
+
|
|
128
|
+
# Run the tool
|
|
129
|
+
docker run -v $(pwd):/app/data cod8a uml -p /app/data/src
|
|
130
|
+
```
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cod8a"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "cod8a is a code documentation and visualization tool for Python and .NET projects"
|
|
5
5
|
authors = [
|
|
6
|
-
{ name = "Akumbom",email = "akumbom5ma@gmail.com"}
|
|
6
|
+
{ name = "Marietta Akumbom",email = "akumbom5ma@gmail.com"}
|
|
7
7
|
]
|
|
8
8
|
maintainers = [
|
|
9
9
|
{ name = "Marietta Akumbom" }
|
|
@@ -17,14 +17,18 @@ dependencies = [
|
|
|
17
17
|
"pydantic (>=2.13.3,<3.0.0)"
|
|
18
18
|
]
|
|
19
19
|
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://github.com/marietta-a/cod8a"
|
|
22
|
+
Repository = "https://github.com/marietta-a/cod8a"
|
|
23
|
+
|
|
20
24
|
[tool.poetry]
|
|
21
25
|
packages = [{include = "cod8a", from = "src"}]
|
|
22
26
|
include = [
|
|
23
|
-
{ path = "src/cod8a/dotnet/
|
|
24
|
-
{ path = "src/cod8a/dotnet/
|
|
27
|
+
{ path = "src/cod8a/dotnet/CodeAnalyzer/bin/Release/net10.0/*.dll", format = ["wheel"] },
|
|
28
|
+
{ path = "src/cod8a/dotnet/CodeAnalyzer/bin/Release/net10.0/*.json", format = ["wheel"] }
|
|
25
29
|
]
|
|
26
30
|
|
|
27
|
-
[
|
|
31
|
+
[project.scripts]
|
|
28
32
|
cod8a = "cod8a.cli:main"
|
|
29
33
|
cde8a = "cod8a.cli:doc_main"
|
|
30
34
|
|
|
@@ -43,7 +43,9 @@ def cli():
|
|
|
43
43
|
help='The type of diagram to generate (default: class).')
|
|
44
44
|
@click.option('-o', '--output',
|
|
45
45
|
help='Optional output file path. If not provided, you will be prompted to save to the Downloads folder.')
|
|
46
|
-
|
|
46
|
+
@click.option('-s', '--summarize', is_flag=True,
|
|
47
|
+
help='Summarize the diagram by omitting details like fields and methods (recommended for large files).')
|
|
48
|
+
def uml(path, diagram_type, output, summarize):
|
|
47
49
|
"""Generate UML diagram (Mermaid format)."""
|
|
48
50
|
struct = extract_structure(path)
|
|
49
51
|
base_name = os.path.basename(path or os.getcwd())
|
|
@@ -53,15 +55,30 @@ def uml(path, diagram_type, output):
|
|
|
53
55
|
click.echo("Error: Could not extract structure.")
|
|
54
56
|
return
|
|
55
57
|
|
|
58
|
+
# Auto-summarize if not explicitly requested
|
|
59
|
+
if not summarize:
|
|
60
|
+
classes = []
|
|
61
|
+
if hasattr(struct, 'files'):
|
|
62
|
+
classes = [c for f in struct.files for c in f.classes]
|
|
63
|
+
elif hasattr(struct, 'classes'):
|
|
64
|
+
classes = struct.classes
|
|
65
|
+
elif isinstance(struct, list):
|
|
66
|
+
classes = [c for f in struct for c in f.classes]
|
|
67
|
+
|
|
68
|
+
total_members = sum(len(c.fields) + len(c.methods) for c in classes)
|
|
69
|
+
if len(classes) > 50 or total_members > 250:
|
|
70
|
+
click.echo("Note: Large file/project detected. Auto-summarizing diagram for better visualization.")
|
|
71
|
+
summarize = True
|
|
72
|
+
|
|
56
73
|
canon_type = "class"
|
|
57
74
|
if DiagramType.FLOWCHART.value.startswith(diagram_type):
|
|
58
|
-
diagram = generate_flowchart_diagram(struct, base_name)
|
|
75
|
+
diagram = generate_flowchart_diagram(struct, base_name, summarize)
|
|
59
76
|
canon_type = "flowchart"
|
|
60
77
|
elif DiagramType.SEQUENCE.value.startswith(diagram_type):
|
|
61
|
-
diagram = generate_sequence_diagram(struct)
|
|
78
|
+
diagram = generate_sequence_diagram(struct, summarize)
|
|
62
79
|
canon_type = "sequence"
|
|
63
80
|
else:
|
|
64
|
-
diagram = generate_class_diagram(struct)
|
|
81
|
+
diagram = generate_class_diagram(struct, summarize)
|
|
65
82
|
canon_type = "class"
|
|
66
83
|
|
|
67
84
|
print(diagram)
|
|
@@ -19,12 +19,13 @@ namespace CodeAnalyzer.Parsers
|
|
|
19
19
|
public sealed partial class FileParser<T> : BaseParser<T> where T : FileStructure
|
|
20
20
|
{
|
|
21
21
|
|
|
22
|
-
public override string Name { get; init; }
|
|
22
|
+
public override required string Name { get; init; }
|
|
23
23
|
|
|
24
24
|
/// <summary>
|
|
25
25
|
/// Gets the code associated with this instance.
|
|
26
26
|
/// </summary>
|
|
27
|
-
public string FilePath { get; init; }
|
|
27
|
+
public required string FilePath { get; init; }
|
|
28
|
+
public int Id { get; init; } = 1;
|
|
28
29
|
public override T Parse()
|
|
29
30
|
{
|
|
30
31
|
try
|
|
@@ -37,8 +38,7 @@ namespace CodeAnalyzer.Parsers
|
|
|
37
38
|
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
|
|
38
39
|
|
|
39
40
|
|
|
40
|
-
var
|
|
41
|
-
var fileName = Path.GetFileName(directory);
|
|
41
|
+
var fileName = Name ?? Path.GetFileName(FilePath);
|
|
42
42
|
var id = 0;
|
|
43
43
|
var usings = root.Usings.Select(b =>
|
|
44
44
|
{
|
|
@@ -47,7 +47,7 @@ namespace CodeAnalyzer.Parsers
|
|
|
47
47
|
});
|
|
48
48
|
var fileStructure = new FileStructure
|
|
49
49
|
{
|
|
50
|
-
Id =
|
|
50
|
+
Id = Id,
|
|
51
51
|
Name = fileName,
|
|
52
52
|
UsingDirectives = usings.ToList(),
|
|
53
53
|
};
|
|
@@ -57,7 +57,7 @@ namespace CodeAnalyzer.Parsers
|
|
|
57
57
|
|
|
58
58
|
return (T)fileStructure;
|
|
59
59
|
}
|
|
60
|
-
catch
|
|
60
|
+
catch
|
|
61
61
|
{
|
|
62
62
|
throw;
|
|
63
63
|
}
|
|
@@ -76,7 +76,7 @@ namespace CodeAnalyzer.Parsers
|
|
|
76
76
|
_ => string.Empty
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
|
-
catch
|
|
79
|
+
catch
|
|
80
80
|
{
|
|
81
81
|
throw;
|
|
82
82
|
}
|
|
@@ -98,7 +98,10 @@ namespace CodeAnalyzer.Parsers
|
|
|
98
98
|
var relationships = new List<RelationShip>();
|
|
99
99
|
|
|
100
100
|
var baseTypes = classDeclaration.BaseList?.Types;
|
|
101
|
-
int
|
|
101
|
+
int relId = 0;
|
|
102
|
+
int fieldId = 0;
|
|
103
|
+
int methodId = 0;
|
|
104
|
+
int classId = 0;
|
|
102
105
|
|
|
103
106
|
if(baseTypes is not null)
|
|
104
107
|
{
|
|
@@ -112,19 +115,19 @@ namespace CodeAnalyzer.Parsers
|
|
|
112
115
|
_ => b.Type.ToString().Split('<')[0]
|
|
113
116
|
};
|
|
114
117
|
var type = parent.StartsWith("I") && parent.Length > 1 && char.IsUpper(parent[1]) ? "Interface" : "Class";
|
|
115
|
-
return new RelationShip (++
|
|
118
|
+
return new RelationShip (++relId, type, parent);
|
|
116
119
|
})
|
|
117
120
|
);
|
|
118
121
|
}
|
|
119
122
|
|
|
120
123
|
// Extract standard fields and properties
|
|
121
|
-
fieldStructures.AddRange(classDeclaration.DescendantNodes().OfType<FieldDeclarationSyntax>().Select(f => new FieldStructure(
|
|
122
|
-
fieldStructures.AddRange(classDeclaration.DescendantNodes().OfType<PropertyDeclarationSyntax>().Select(f => new FieldStructure(
|
|
124
|
+
fieldStructures.AddRange(classDeclaration.DescendantNodes().OfType<FieldDeclarationSyntax>().Select(f => new FieldStructure(++fieldId, f.Declaration.Variables.First().Identifier.Text, f.Modifiers.ToFullString().Trim(), f.Declaration.Type.ToString(), f.GetLeadingTrivia().ToString().Trim())));
|
|
125
|
+
fieldStructures.AddRange(classDeclaration.DescendantNodes().OfType<PropertyDeclarationSyntax>().Select(f => new FieldStructure(++fieldId, f.Identifier.Text, f.Modifiers.ToFullString().Trim(), f.Type.ToString(), f.GetLeadingTrivia().ToString().Trim())));
|
|
123
126
|
|
|
124
127
|
// Extract record positional parameters as properties
|
|
125
128
|
if (classDeclaration is RecordDeclarationSyntax recordDecl && recordDecl.ParameterList != null)
|
|
126
129
|
{
|
|
127
|
-
fieldStructures.AddRange(recordDecl.ParameterList.Parameters.Select(p => new FieldStructure(
|
|
130
|
+
fieldStructures.AddRange(recordDecl.ParameterList.Parameters.Select(p => new FieldStructure(++fieldId, p.Identifier.Text, "public", p.Type?.ToString() ?? "", p.GetLeadingTrivia().ToString().Trim())));
|
|
128
131
|
}
|
|
129
132
|
|
|
130
133
|
foreach (var method in classDeclaration.Members.OfType<MethodDeclarationSyntax>())
|
|
@@ -132,14 +135,34 @@ namespace CodeAnalyzer.Parsers
|
|
|
132
135
|
var parameters = method.ParameterList.Parameters.Select(p => new ParameterStructure(p.Identifier.Text, p.Modifiers.ToFullString().Trim(), p.Type?.ToString() ?? "", p.GetLeadingTrivia().ToString().Trim())).ToList();
|
|
133
136
|
parameterStructures.AddRange(parameters);
|
|
134
137
|
|
|
135
|
-
methodStructures.Add(new MethodStructure(
|
|
138
|
+
methodStructures.Add(new MethodStructure(++methodId, method.Identifier.Text, method.Modifiers.ToFullString().Trim(), method.ReturnType.ToString(), parameters, method.GetLeadingTrivia().ToString().Trim()));
|
|
136
139
|
}
|
|
137
|
-
|
|
140
|
+
|
|
141
|
+
var className = classDeclaration.Identifier.Text;
|
|
142
|
+
if (string.IsNullOrEmpty(className) && classDeclaration.Keyword.Text == "extension")
|
|
143
|
+
{
|
|
144
|
+
var parameterList = classDeclaration.ChildNodes().OfType<ParameterListSyntax>().FirstOrDefault();
|
|
145
|
+
if (parameterList != null && parameterList.Parameters.Any())
|
|
146
|
+
{
|
|
147
|
+
var extendedType = parameterList.Parameters[0].Type;
|
|
148
|
+
if (extendedType != null)
|
|
149
|
+
{
|
|
150
|
+
className = $"{extendedType}ExtensionBlock";
|
|
151
|
+
relationships.Add(new RelationShip(++relId, "Extension", extendedType.ToString()));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else
|
|
155
|
+
{
|
|
156
|
+
className = "extension";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
classes.Add(new ClassStructure(++classId, className, methodStructures, fieldStructures, classDeclaration.Keyword.ToFullString().Trim(), relationships, classDeclaration.GetLeadingTrivia().ToString().Trim()));
|
|
138
161
|
}
|
|
139
162
|
|
|
140
163
|
return classes;
|
|
141
164
|
}
|
|
142
|
-
catch
|
|
165
|
+
catch
|
|
143
166
|
{
|
|
144
167
|
throw;
|
|
145
168
|
}
|
|
@@ -10,24 +10,30 @@ namespace CodeAnalyzer.Parsers
|
|
|
10
10
|
{
|
|
11
11
|
public sealed partial class ProjectParser<T> : BaseParser<T> where T : ProjectStructure
|
|
12
12
|
{
|
|
13
|
-
public override string Name { get; init; }
|
|
13
|
+
public override required string Name { get; init; }
|
|
14
14
|
|
|
15
|
-
public string[] FilePaths { get; init; }
|
|
15
|
+
public required string[] FilePaths { get; init; }
|
|
16
16
|
|
|
17
17
|
public override T Parse()
|
|
18
18
|
{
|
|
19
19
|
try
|
|
20
20
|
{
|
|
21
|
+
var fileId = 1;
|
|
21
22
|
var projectStructure = new ProjectStructure
|
|
22
23
|
{
|
|
23
24
|
Id = 1,
|
|
24
25
|
Name = Name,
|
|
25
|
-
Files = FilePaths.Select(b => new FileParser<FileStructure>
|
|
26
|
+
Files = FilePaths.Select(b => new FileParser<FileStructure>
|
|
27
|
+
{
|
|
28
|
+
FilePath = b,
|
|
29
|
+
Name = Path.GetFileName(b),
|
|
30
|
+
Id = fileId++
|
|
31
|
+
}.Parse()).ToList(),
|
|
26
32
|
};
|
|
27
33
|
|
|
28
34
|
return (T) projectStructure;
|
|
29
35
|
}
|
|
30
|
-
catch
|
|
36
|
+
catch
|
|
31
37
|
{
|
|
32
38
|
throw;
|
|
33
39
|
}
|
|
@@ -7,7 +7,7 @@ namespace CodeAnalyzer.Parsers
|
|
|
7
7
|
{
|
|
8
8
|
public sealed partial class SolutionParser<T> : BaseParser<T> where T : SolutionStructure
|
|
9
9
|
{
|
|
10
|
-
public override string Name { get; init; }
|
|
10
|
+
public override required string Name { get; init; }
|
|
11
11
|
|
|
12
12
|
public override T Parse()
|
|
13
13
|
{
|
{cod8a-0.1.0/src/cod8a/dotnet/CodeAnalysis → cod8a-0.2.0/src/cod8a/dotnet/CodeAnalyzer}/Program.cs
RENAMED
|
@@ -9,35 +9,37 @@ class Program
|
|
|
9
9
|
{
|
|
10
10
|
static void Main(string[] args)
|
|
11
11
|
{
|
|
12
|
-
|
|
13
|
-
var fileName = args.Length > 0 ? args[0] : null;
|
|
14
|
-
//Check if path is directory
|
|
15
|
-
var fileAttr = File.GetAttributes(fileName!);
|
|
16
|
-
var isDirectory = fileAttr.HasFlag(FileAttributes.Directory);
|
|
12
|
+
var path = args.Length > 0 ? args[0] : null;
|
|
17
13
|
|
|
18
|
-
|
|
19
|
-
if(string.IsNullOrEmpty(fileName))
|
|
14
|
+
if(string.IsNullOrEmpty(path))
|
|
20
15
|
{
|
|
21
16
|
Console.Error.WriteLine(@"No file or project specified. Please provide a file/directory
|
|
22
17
|
containing ...cs, .csproj, or .sln file.");
|
|
23
18
|
return;
|
|
24
19
|
}
|
|
25
20
|
|
|
26
|
-
if
|
|
27
|
-
|
|
21
|
+
//Check if path is directory
|
|
22
|
+
var fileAttr = File.GetAttributes(path!);
|
|
23
|
+
var isDirectory = fileAttr.HasFlag(FileAttributes.Directory);
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if (!File.Exists(path) && !isDirectory){
|
|
27
|
+
Console.Error.WriteLine($"File not found: {path}");
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
var json = "";
|
|
32
|
+
var fileName = Path.GetFileName(path);
|
|
32
33
|
|
|
33
|
-
if (isDirectory ||
|
|
34
|
+
if (isDirectory || path.EndsWith(".csproj") || path.EndsWith(".sln") || path.EndsWith(".slnx"))
|
|
34
35
|
{
|
|
35
|
-
var
|
|
36
|
+
var directoryPath = isDirectory ? path : Path.GetDirectoryName(path)!;
|
|
37
|
+
var csFiles = Directory.GetFiles(directoryPath, "*.cs", SearchOption.TopDirectoryOnly).Where(f => !f.Contains("obj", StringComparison.CurrentCultureIgnoreCase)).ToArray();
|
|
36
38
|
|
|
37
39
|
BaseParser<ProjectStructure> projectStructure = new ProjectParser<ProjectStructure>()
|
|
38
40
|
{
|
|
39
41
|
FilePaths = csFiles,
|
|
40
|
-
Name =
|
|
42
|
+
Name = fileName,
|
|
41
43
|
};
|
|
42
44
|
|
|
43
45
|
json = JsonSerializer.Serialize(projectStructure.Parse(), new JsonSerializerOptions { WriteIndented = false });
|
|
@@ -46,13 +48,14 @@ class Program
|
|
|
46
48
|
{
|
|
47
49
|
BaseParser<FileStructure> fileStructure = new FileParser<FileStructure>()
|
|
48
50
|
{
|
|
49
|
-
FilePath =
|
|
50
|
-
Name =
|
|
51
|
+
FilePath = path,
|
|
52
|
+
Name = fileName,
|
|
51
53
|
};
|
|
52
54
|
|
|
53
55
|
json = JsonSerializer.Serialize(fileStructure.Parse(), new JsonSerializerOptions { WriteIndented = false });
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
Console.WriteLine(json);
|
|
59
|
+
|
|
57
60
|
}
|
|
58
61
|
}
|
|
@@ -14,17 +14,17 @@ namespace CodeAnalyzer.Models
|
|
|
14
14
|
/// <summary>
|
|
15
15
|
/// <see langword="nameof"/>of the namespace, e.g., <c>MyNamespace</c>.
|
|
16
16
|
/// </summary>
|
|
17
|
-
public string Name { get; set; }
|
|
17
|
+
public required string Name { get; set; }
|
|
18
18
|
|
|
19
19
|
/// <summary>
|
|
20
20
|
/// Gets or sets the collection of using directives associated with the current context.
|
|
21
21
|
/// </summary>
|
|
22
|
-
public List<UsingDirective
|
|
22
|
+
public List<UsingDirective>? UsingDirectives { get; set; }
|
|
23
23
|
|
|
24
24
|
/// <summary>
|
|
25
25
|
/// Gets or sets the collection of classes associated with the current instance.
|
|
26
26
|
/// </summary>
|
|
27
|
-
public List<ClassStructure
|
|
27
|
+
public List<ClassStructure>? Classes { get; set; }
|
|
28
28
|
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -14,16 +14,16 @@ namespace CodeAnalyzer.Models
|
|
|
14
14
|
/// <summary>
|
|
15
15
|
/// Name of the Project
|
|
16
16
|
/// </summary>
|
|
17
|
-
public string Name { get; set; }
|
|
17
|
+
public required string Name { get; set; }
|
|
18
18
|
|
|
19
19
|
/// <summary>
|
|
20
20
|
/// Project Description
|
|
21
21
|
/// </summary>
|
|
22
|
-
public string Description { get; set; }
|
|
22
|
+
public string? Description { get; set; }
|
|
23
23
|
|
|
24
24
|
/// <summary>
|
|
25
25
|
/// Gets or sets the collection of files associated with the Project.
|
|
26
26
|
/// </summary>
|
|
27
|
-
public List<FileStructure> Files { get; set; }
|
|
27
|
+
public required List<FileStructure> Files { get; set; }
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -13,16 +13,16 @@ namespace CodeAnalyzer.Models
|
|
|
13
13
|
/// <summary>
|
|
14
14
|
/// Gets or sets the name of the Solution
|
|
15
15
|
/// </summary>
|
|
16
|
-
public string Name { get; set; }
|
|
16
|
+
public required string Name { get; set; }
|
|
17
17
|
|
|
18
18
|
/// <summary>
|
|
19
19
|
/// Gets or sets the description associated with the object.
|
|
20
20
|
/// </summary>
|
|
21
|
-
public string Description { get; set; }
|
|
21
|
+
public string? Description { get; set; }
|
|
22
22
|
|
|
23
23
|
/// <summary>
|
|
24
24
|
/// Gets or sets the collection of projects within this solution
|
|
25
25
|
/// </summary>
|
|
26
|
-
public List<ProjectStructure> Projects { get; set; }
|
|
26
|
+
public required List<ProjectStructure> Projects { get; set; }
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.IO;
|
|
3
|
+
using System.Linq;
|
|
4
|
+
using Xunit;
|
|
5
|
+
using CodeAnalyzer.Parsers;
|
|
6
|
+
using CodeAnalyzer.Models;
|
|
7
|
+
|
|
8
|
+
namespace MermaidTests.Mermaid
|
|
9
|
+
{
|
|
10
|
+
public class ExtensionTypeTest
|
|
11
|
+
{
|
|
12
|
+
[Fact]
|
|
13
|
+
public void Parse_ExtensionType_HandlesEmptyIdentifier()
|
|
14
|
+
{
|
|
15
|
+
// Arrange
|
|
16
|
+
var testCode = @"
|
|
17
|
+
namespace TestNamespace
|
|
18
|
+
{
|
|
19
|
+
extension(Color)
|
|
20
|
+
{
|
|
21
|
+
public static Color GreenSmile()
|
|
22
|
+
{
|
|
23
|
+
return Color.FromArgb(83, 255, 26);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}";
|
|
27
|
+
|
|
28
|
+
var filePath = Path.GetTempFileName() + ".cs";
|
|
29
|
+
File.WriteAllText(filePath, testCode);
|
|
30
|
+
|
|
31
|
+
try
|
|
32
|
+
{
|
|
33
|
+
var parser = new FileParser<FileStructure>
|
|
34
|
+
{
|
|
35
|
+
FilePath = filePath,
|
|
36
|
+
Name = Path.GetFileName(filePath)
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Act
|
|
40
|
+
var result = parser.Parse();
|
|
41
|
+
|
|
42
|
+
// Assert
|
|
43
|
+
Assert.NotNull(result);
|
|
44
|
+
Assert.Single(result.Classes);
|
|
45
|
+
var extensionClass = result.Classes.First();
|
|
46
|
+
|
|
47
|
+
// This is what the user says is happening: it's empty.
|
|
48
|
+
// We want it to NOT be empty, or at least handle it gracefully.
|
|
49
|
+
Assert.Equal("extension(Color)", extensionClass.Name);
|
|
50
|
+
|
|
51
|
+
// Verify Relationship
|
|
52
|
+
Assert.NotNull(extensionClass.Relationships);
|
|
53
|
+
Assert.Single(extensionClass.Relationships);
|
|
54
|
+
var rel = extensionClass.Relationships.First();
|
|
55
|
+
Assert.Equal("Extension", rel.Type);
|
|
56
|
+
Assert.Equal("Color", rel.AssociatedItem);
|
|
57
|
+
}
|
|
58
|
+
finally
|
|
59
|
+
{
|
|
60
|
+
if (File.Exists(filePath)) File.Delete(filePath);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -9,7 +9,7 @@ class ClassDiagramGenerator:
|
|
|
9
9
|
Generates a Mermaid class diagram from a JSON representation of code structure.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
def generate(self, data: Union[FileStructure, ProjectStructure, List[FileStructure]]) -> str:
|
|
12
|
+
def generate(self, data: Union[FileStructure, ProjectStructure, List[FileStructure]], summarize: bool = False) -> str:
|
|
13
13
|
if not isinstance(data, FileStructure | ProjectStructure | list):
|
|
14
14
|
return "Error: Invalid data structure"
|
|
15
15
|
|
|
@@ -19,7 +19,7 @@ class ClassDiagramGenerator:
|
|
|
19
19
|
mermaid_lines = ["classDiagram"]
|
|
20
20
|
# 1. Generate Class Definitions
|
|
21
21
|
for cls in all_classes:
|
|
22
|
-
mermaid_lines.extend(self._generate_class_block(cls))
|
|
22
|
+
mermaid_lines.extend(self._generate_class_block(cls, summarize))
|
|
23
23
|
|
|
24
24
|
# 2. Generate Relationships
|
|
25
25
|
mermaid_lines.extend(self._generate_relationships(all_classes))
|
|
@@ -58,7 +58,10 @@ class ClassDiagramGenerator:
|
|
|
58
58
|
|
|
59
59
|
return classes
|
|
60
60
|
|
|
61
|
-
def _generate_class_block(self, cls: ClassStructure) -> List[str]:
|
|
61
|
+
def _generate_class_block(self, cls: ClassStructure, summarize: bool = False) -> List[str]:
|
|
62
|
+
if summarize:
|
|
63
|
+
return [f" class {cls.name}", ""]
|
|
64
|
+
|
|
62
65
|
lines = [f" class {cls.name} {{"]
|
|
63
66
|
|
|
64
67
|
# Fields
|
|
@@ -129,6 +132,9 @@ class ClassDiagramGenerator:
|
|
|
129
132
|
label = "inherits"
|
|
130
133
|
if "interface" in relation.type.lower() or "implements" in relation.type.lower():
|
|
131
134
|
label = "implements"
|
|
135
|
+
elif "extension" in relation.type.lower():
|
|
136
|
+
label = "extends"
|
|
137
|
+
connector = "<.."
|
|
132
138
|
|
|
133
139
|
rel_lines.append(f" {relation.parent_name} {connector} {cls_name} : {label}")
|
|
134
140
|
|
|
@@ -153,10 +159,10 @@ class ClassDiagramGenerator:
|
|
|
153
159
|
|
|
154
160
|
return sorted(list(set(rel_lines)))
|
|
155
161
|
|
|
156
|
-
def generate_class_diagram(data: FileStructure | ProjectStructure) -> str:
|
|
162
|
+
def generate_class_diagram(data: FileStructure | ProjectStructure, summarize: bool = False) -> str:
|
|
157
163
|
print("calling uml class generator ...")
|
|
158
164
|
generator = ClassDiagramGenerator()
|
|
159
|
-
return generator.generate(data)
|
|
165
|
+
return generator.generate(data, summarize)
|
|
160
166
|
|
|
161
167
|
if __name__ == "__main__":
|
|
162
168
|
import sys
|
|
@@ -8,9 +8,9 @@ class FlowchartDiagramGenerator:
|
|
|
8
8
|
Generates a readable Mermaid flowchart from code structure.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
def generate(self, data: Union['FileStructure', 'ProjectStructure', List['FileStructure']], file_name: str) -> str:
|
|
11
|
+
def generate(self, data: Union['FileStructure', 'ProjectStructure', List['FileStructure']], file_name: str, summarize: bool = False) -> str:
|
|
12
12
|
mermaid_lines = ["graph TD"]
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
# Track unique IDs to avoid conflicts in Mermaid
|
|
15
15
|
self.counter = 0
|
|
16
16
|
|
|
@@ -21,31 +21,38 @@ class FlowchartDiagramGenerator:
|
|
|
21
21
|
elif isinstance(data, list): files = data
|
|
22
22
|
|
|
23
23
|
for file in files:
|
|
24
|
-
file_id = f"file_{self._get_id()}"
|
|
24
|
+
# file_id = f"file_{self._get_id()}"
|
|
25
|
+
file_id = f"file_{file.id}" if file.id else f"file_{self._get_id()}"
|
|
25
26
|
mermaid_lines.append(f' {file_id}["File: {file.name}"]')
|
|
26
|
-
|
|
27
|
+
|
|
27
28
|
for cls in file.classes:
|
|
28
|
-
cls_id = f"cls_{self._get_id()}"
|
|
29
|
+
# cls_id = f"cls_{self._get_id()}"
|
|
30
|
+
cls_id = f"cls_{cls.id}" if cls.id else f"cls_{self._get_id()}"
|
|
29
31
|
mermaid_lines.append(f' {file_id} --> {cls_id}["Class: {cls.name}"]')
|
|
30
|
-
|
|
32
|
+
|
|
33
|
+
if summarize:
|
|
34
|
+
continue
|
|
35
|
+
|
|
31
36
|
# Exclude private Properties
|
|
32
37
|
fields = [f for f in cls.fields if f.modifier not in "private"]
|
|
33
38
|
# If too many fields, don't overwhelm the diagram truncate
|
|
34
39
|
fields = fields[:10] if len(fields) > 10 else fields
|
|
35
40
|
for field in fields:
|
|
36
|
-
f_id = f"f_{self._get_id()}"
|
|
41
|
+
# f_id = f"f_{self._get_id()}"
|
|
42
|
+
f_id = f"f_{field.id}" if field.id else f"f_{self._get_id()}"
|
|
37
43
|
mermaid_lines.append(f' {cls_id} --> {f_id}["{field.name} ({field.type})"]')
|
|
38
44
|
|
|
39
45
|
# Methods: Always helpful to see
|
|
40
46
|
for method in cls.methods:
|
|
41
|
-
m_id = f"m_{self._get_id()}"
|
|
47
|
+
# m_id = f"m_{self._get_id()}"
|
|
48
|
+
m_id = f"m_{method.id}" if method.id else f"m_{self._get_id()}"
|
|
42
49
|
mermaid_lines.append(f' {cls_id} --> {m_id}{{"{method.name}()"}}')
|
|
43
|
-
|
|
50
|
+
|
|
44
51
|
return "\n".join(mermaid_lines)
|
|
45
52
|
|
|
46
53
|
def _get_id(self) -> int:
|
|
47
54
|
self.counter += 1
|
|
48
55
|
return self.counter
|
|
49
56
|
|
|
50
|
-
def generate_flowchart_diagram(data, file_name) -> str:
|
|
51
|
-
return FlowchartDiagramGenerator().generate(data, file_name)
|
|
57
|
+
def generate_flowchart_diagram(data, file_name, summarize: bool = False) -> str:
|
|
58
|
+
return FlowchartDiagramGenerator().generate(data, file_name, summarize)
|
|
@@ -9,7 +9,7 @@ class SequenceDiagramGenerator:
|
|
|
9
9
|
def _sanitize(self, name: str) -> str:
|
|
10
10
|
return re.sub(r'[^a-zA-Z0-9_ ]', '', name)
|
|
11
11
|
|
|
12
|
-
def generate(self, data: Union['FileStructure', 'ProjectStructure', list['FileStructure']]) -> str:
|
|
12
|
+
def generate(self, data: Union['FileStructure', 'ProjectStructure', list['FileStructure']], summarize: bool = False) -> str:
|
|
13
13
|
classes = self._extract_classes(data)
|
|
14
14
|
|
|
15
15
|
mermaid_lines = ["sequenceDiagram", " autonumber"]
|
|
@@ -37,13 +37,15 @@ class SequenceDiagramGenerator:
|
|
|
37
37
|
# Exclude private properties
|
|
38
38
|
fields = [f for f in cls.fields if f.modifier != "private"]
|
|
39
39
|
mermaid_lines.append(f" C->>{cls_alias}: Create {cls.name}")
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
|
|
41
|
+
if not summarize:
|
|
42
|
+
for field in cls.fields[:20]: # Limit to 20 fields to avoid huge diagrams
|
|
43
|
+
if not field.name.startswith('_'):
|
|
44
|
+
mermaid_lines.append(f" {cls_alias}->>{cls_alias}: Set {self._sanitize(field.name)}")
|
|
43
45
|
else:
|
|
44
46
|
# Exclude private methods
|
|
45
|
-
methods = [m for m in cls.methods if m.modifier != "private"]
|
|
46
|
-
for method in methods:
|
|
47
|
+
# methods = [m for m in cls.methods if m.modifier != "private"]
|
|
48
|
+
for method in cls.methods:
|
|
47
49
|
if method.name.lower() in["dispose", "tostring", "equals", "gethashcode", "gettype"]:
|
|
48
50
|
continue
|
|
49
51
|
|
|
@@ -70,8 +72,8 @@ class SequenceDiagramGenerator:
|
|
|
70
72
|
if not type_str: return ""
|
|
71
73
|
return re.split(r'[<\[]', type_str)[0].replace('?', '').split('.')[-1].strip()
|
|
72
74
|
|
|
73
|
-
def generate_sequence_diagram(data) -> str:
|
|
74
|
-
return SequenceDiagramGenerator().generate(data)
|
|
75
|
+
def generate_sequence_diagram(data, summarize: bool = False) -> str:
|
|
76
|
+
return SequenceDiagramGenerator().generate(data, summarize)
|
|
75
77
|
|
|
76
78
|
if __name__ == "__main__":
|
|
77
79
|
import sys
|
|
@@ -9,10 +9,10 @@ from cod8a.parsers.python_parser import PythonParser
|
|
|
9
9
|
from cod8a.models.models import FileStructure, ProjectStructure
|
|
10
10
|
|
|
11
11
|
# Path to the C# analyzer project
|
|
12
|
-
DOTNET_ANALYZER_PATH = os.path.join(os.path.dirname(__file__), "..", "dotnet", "
|
|
12
|
+
DOTNET_ANALYZER_PATH = os.path.join(os.path.dirname(__file__), "..", "dotnet", "CodeAnalyzer", "CodeAnalyzer.csproj")
|
|
13
13
|
|
|
14
14
|
def _get_parser(path: str):
|
|
15
|
-
print(f"
|
|
15
|
+
print(f"Analyzing File ...")
|
|
16
16
|
isDotnetParser = any(path.endswith(ext) for ext in [".cs", ".csproj", ".sln"]) or os.path.isdir(path) and any(f.endswith(".cs") for _, _, files in os.walk(path) for f in files)
|
|
17
17
|
if isDotnetParser:
|
|
18
18
|
return DotnetParser(DOTNET_ANALYZER_PATH)
|
|
@@ -3,6 +3,7 @@ import json
|
|
|
3
3
|
import os
|
|
4
4
|
import shutil
|
|
5
5
|
from typing import Union, List
|
|
6
|
+
from pathlib import Path
|
|
6
7
|
from cod8a.models.models import (
|
|
7
8
|
FileStructure, ProjectStructure, ClassStructure,
|
|
8
9
|
MethodStructure, FieldStructure, ParameterStructure,
|
|
@@ -12,6 +13,7 @@ from cod8a.models.models import (
|
|
|
12
13
|
class DotnetParser:
|
|
13
14
|
def __init__(self, analyzer_path: str):
|
|
14
15
|
self.analyzer_path = analyzer_path
|
|
16
|
+
self.file_name = ""
|
|
15
17
|
|
|
16
18
|
def parse(self, path: str) -> Union[FileStructure, ProjectStructure]:
|
|
17
19
|
if not shutil.which("dotnet"):
|
|
@@ -19,9 +21,14 @@ class DotnetParser:
|
|
|
19
21
|
|
|
20
22
|
abs_path = os.path.abspath(path)
|
|
21
23
|
cwd = os.path.dirname(self.analyzer_path)
|
|
24
|
+
self.file_name = Path(abs_path).stem
|
|
25
|
+
|
|
26
|
+
wdir = os.getcwd()
|
|
27
|
+
print(wdir)
|
|
28
|
+
|
|
22
29
|
|
|
23
30
|
# Look for a compiled DLL first (production/distribution mode)
|
|
24
|
-
# Expected path: src/cod8a/dotnet/
|
|
31
|
+
# Expected path: src/cod8a/dotnet/CodeAnalyzer/bin/Release/net10.0/CodeAnalyzer.dll
|
|
25
32
|
dll_path = os.path.join(cwd, "bin", "Release", "net10.0", "CodeAnalyzer.dll")
|
|
26
33
|
|
|
27
34
|
if os.path.exists(dll_path):
|
|
@@ -31,6 +38,7 @@ class DotnetParser:
|
|
|
31
38
|
args = ["dotnet", "run", "--project", self.analyzer_path, "--", abs_path]
|
|
32
39
|
|
|
33
40
|
result = subprocess.run(args, capture_output=True, text=True, cwd=cwd)
|
|
41
|
+
|
|
34
42
|
if result.returncode != 0:
|
|
35
43
|
raise Exception(f"Dotnet analyzer failed: {result.stderr}")
|
|
36
44
|
|
|
@@ -50,7 +58,7 @@ class DotnetParser:
|
|
|
50
58
|
|
|
51
59
|
def _map_project(self, data: dict) -> ProjectStructure:
|
|
52
60
|
return ProjectStructure(
|
|
53
|
-
name=
|
|
61
|
+
name=self.file_name,
|
|
54
62
|
files=[self._map_file(f) for f in data.get("Files", [])]
|
|
55
63
|
)
|
|
56
64
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|