reveal-cli 0.8.0__py3-none-any.whl
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.
- plugins/c-header.yaml +89 -0
- plugins/gdscript.yaml +96 -0
- plugins/python.yaml +94 -0
- plugins/yaml.yaml +87 -0
- reveal/__init__.py +26 -0
- reveal/analyzers/__init__.py +39 -0
- reveal/analyzers/bash.py +44 -0
- reveal/analyzers/dockerfile.py +179 -0
- reveal/analyzers/gdscript.py +176 -0
- reveal/analyzers/go.py +13 -0
- reveal/analyzers/javascript.py +21 -0
- reveal/analyzers/jupyter_analyzer.py +230 -0
- reveal/analyzers/markdown.py +79 -0
- reveal/analyzers/nginx.py +185 -0
- reveal/analyzers/python.py +15 -0
- reveal/analyzers/rust.py +13 -0
- reveal/analyzers/toml.py +96 -0
- reveal/analyzers/typescript.py +24 -0
- reveal/analyzers/yaml_json.py +110 -0
- reveal/base.py +267 -0
- reveal/main.py +355 -0
- reveal/tests/__init__.py +1 -0
- reveal/tests/test_json_yaml_line_numbers.py +238 -0
- reveal/tests/test_line_numbers.py +151 -0
- reveal/tests/test_toml_analyzer.py +220 -0
- reveal/tree_view.py +105 -0
- reveal/treesitter.py +281 -0
- reveal_cli-0.8.0.dist-info/METADATA +352 -0
- reveal_cli-0.8.0.dist-info/RECORD +33 -0
- reveal_cli-0.8.0.dist-info/WHEEL +5 -0
- reveal_cli-0.8.0.dist-info/entry_points.txt +2 -0
- reveal_cli-0.8.0.dist-info/licenses/LICENSE +21 -0
- reveal_cli-0.8.0.dist-info/top_level.txt +2 -0
plugins/c-header.yaml
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# C/C++ Header File Plugin
|
|
2
|
+
# Provides 4-level progressive disclosure for .h and .hpp files
|
|
3
|
+
|
|
4
|
+
extension: [.h, .hpp, .hh]
|
|
5
|
+
name: C/C++ Header
|
|
6
|
+
description: C and C++ header files
|
|
7
|
+
icon: 🔧
|
|
8
|
+
|
|
9
|
+
levels:
|
|
10
|
+
0:
|
|
11
|
+
name: metadata
|
|
12
|
+
description: File statistics and basic info
|
|
13
|
+
breadcrumb: "File metadata"
|
|
14
|
+
analyzer: null
|
|
15
|
+
outputs:
|
|
16
|
+
- file_size
|
|
17
|
+
- line_count
|
|
18
|
+
- encoding
|
|
19
|
+
- header_guards
|
|
20
|
+
- include_count
|
|
21
|
+
next_levels: [1, 2, 3]
|
|
22
|
+
|
|
23
|
+
1:
|
|
24
|
+
name: structure
|
|
25
|
+
description: Declarations, includes, and definitions
|
|
26
|
+
breadcrumb: "Includes, defines, declarations"
|
|
27
|
+
analyzer: c_header_structure
|
|
28
|
+
outputs:
|
|
29
|
+
- includes
|
|
30
|
+
- defines
|
|
31
|
+
- typedefs
|
|
32
|
+
- struct_declarations
|
|
33
|
+
- function_declarations
|
|
34
|
+
- class_declarations # C++
|
|
35
|
+
- namespaces # C++
|
|
36
|
+
- template_declarations # C++
|
|
37
|
+
next_levels: [0, 2, 3]
|
|
38
|
+
tips:
|
|
39
|
+
- "Use --grep to find specific declarations"
|
|
40
|
+
- "Level 2 shows function signatures"
|
|
41
|
+
|
|
42
|
+
2:
|
|
43
|
+
name: preview
|
|
44
|
+
description: Declarations with signatures
|
|
45
|
+
breadcrumb: "Function signatures, struct definitions"
|
|
46
|
+
analyzer: c_header_preview
|
|
47
|
+
outputs:
|
|
48
|
+
- function_signatures
|
|
49
|
+
- struct_definitions
|
|
50
|
+
- class_definitions
|
|
51
|
+
- template_definitions
|
|
52
|
+
- documentation_comments
|
|
53
|
+
next_levels: [0, 1, 3]
|
|
54
|
+
tips:
|
|
55
|
+
- "See implementation details with --level 3"
|
|
56
|
+
|
|
57
|
+
3:
|
|
58
|
+
name: full
|
|
59
|
+
description: Complete header content
|
|
60
|
+
breadcrumb: "Complete header file (paged)"
|
|
61
|
+
analyzer: null
|
|
62
|
+
outputs:
|
|
63
|
+
- full_content
|
|
64
|
+
next_levels: [0, 1, 2]
|
|
65
|
+
paging: true
|
|
66
|
+
page_size: 120
|
|
67
|
+
|
|
68
|
+
features:
|
|
69
|
+
grep: true
|
|
70
|
+
context: true
|
|
71
|
+
paging: true
|
|
72
|
+
line_numbers: true
|
|
73
|
+
syntax_highlighting: true
|
|
74
|
+
|
|
75
|
+
analyzer_config:
|
|
76
|
+
parse_comments: true
|
|
77
|
+
extract_doxygen: true
|
|
78
|
+
include_private: true
|
|
79
|
+
detect_extern_c: true
|
|
80
|
+
|
|
81
|
+
examples:
|
|
82
|
+
- command: "reveal mylib.h"
|
|
83
|
+
description: "Show header metadata"
|
|
84
|
+
- command: "reveal mylib.h -l 1"
|
|
85
|
+
description: "Show includes and declarations"
|
|
86
|
+
- command: "reveal mylib.h -l 2 --grep 'process'"
|
|
87
|
+
description: "Preview process function signatures"
|
|
88
|
+
- command: "reveal mylib.h -l 3"
|
|
89
|
+
description: "Show complete header content"
|
plugins/gdscript.yaml
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# GDScript File Plugin
|
|
2
|
+
# Provides 4-level progressive disclosure for Godot Engine .gd files
|
|
3
|
+
|
|
4
|
+
extension: .gd
|
|
5
|
+
name: GDScript
|
|
6
|
+
description: Godot Engine GDScript files with structure analysis
|
|
7
|
+
icon: 🎮
|
|
8
|
+
|
|
9
|
+
# Hierarchical levels - each level reveals more detail
|
|
10
|
+
levels:
|
|
11
|
+
0:
|
|
12
|
+
name: metadata
|
|
13
|
+
description: File statistics and basic information
|
|
14
|
+
breadcrumb: "File metadata (size, encoding, line count)"
|
|
15
|
+
analyzer: null # Built-in metadata analyzer
|
|
16
|
+
outputs:
|
|
17
|
+
- file_size
|
|
18
|
+
- line_count
|
|
19
|
+
- encoding
|
|
20
|
+
- last_modified
|
|
21
|
+
- file_type
|
|
22
|
+
next_levels: [1, 2, 3]
|
|
23
|
+
|
|
24
|
+
1:
|
|
25
|
+
name: structure
|
|
26
|
+
description: Code structure without implementations
|
|
27
|
+
breadcrumb: "Class, signals, exports, functions (structure only)"
|
|
28
|
+
analyzer: gdscript_structure
|
|
29
|
+
outputs:
|
|
30
|
+
- extends
|
|
31
|
+
- class_name
|
|
32
|
+
- signals
|
|
33
|
+
- exports
|
|
34
|
+
- constants
|
|
35
|
+
- variables
|
|
36
|
+
- functions
|
|
37
|
+
next_levels: [0, 2, 3]
|
|
38
|
+
tips:
|
|
39
|
+
- "Use --grep 'function_name' to filter specific functions"
|
|
40
|
+
- "Add --context 2 to see surrounding context"
|
|
41
|
+
|
|
42
|
+
2:
|
|
43
|
+
name: preview
|
|
44
|
+
description: Key elements without full code
|
|
45
|
+
breadcrumb: "Extends, signals, exports, function signatures"
|
|
46
|
+
analyzer: gdscript_preview
|
|
47
|
+
outputs:
|
|
48
|
+
- class_declaration
|
|
49
|
+
- signals
|
|
50
|
+
- exports
|
|
51
|
+
- function_signatures
|
|
52
|
+
- constants
|
|
53
|
+
next_levels: [0, 1, 3]
|
|
54
|
+
tips:
|
|
55
|
+
- "See full implementation with --level 3"
|
|
56
|
+
- "Filter by element name with --grep"
|
|
57
|
+
|
|
58
|
+
3:
|
|
59
|
+
name: full
|
|
60
|
+
description: Complete source code with paging
|
|
61
|
+
breadcrumb: "Complete file contents (paged)"
|
|
62
|
+
analyzer: null # Built-in full content reader
|
|
63
|
+
outputs:
|
|
64
|
+
- full_source
|
|
65
|
+
next_levels: [0, 1, 2]
|
|
66
|
+
paging: true
|
|
67
|
+
page_size: 120
|
|
68
|
+
tips:
|
|
69
|
+
- "Use --grep to filter lines"
|
|
70
|
+
- "Add --context N for surrounding lines"
|
|
71
|
+
|
|
72
|
+
# Features available at all levels
|
|
73
|
+
features:
|
|
74
|
+
grep: true
|
|
75
|
+
context: true
|
|
76
|
+
paging: true
|
|
77
|
+
line_numbers: true
|
|
78
|
+
syntax_highlighting: false
|
|
79
|
+
|
|
80
|
+
# Analyzer configuration
|
|
81
|
+
analyzer_config:
|
|
82
|
+
include_lifecycle_funcs: true
|
|
83
|
+
include_private_vars: true
|
|
84
|
+
parse_comments: true
|
|
85
|
+
extract_signals: true
|
|
86
|
+
|
|
87
|
+
# Examples shown in help text
|
|
88
|
+
examples:
|
|
89
|
+
- command: "reveal player.gd"
|
|
90
|
+
description: "Show file metadata (level 0)"
|
|
91
|
+
- command: "reveal player.gd -l 1"
|
|
92
|
+
description: "Show code structure (signals, exports, functions)"
|
|
93
|
+
- command: "reveal player.gd -l 2 --grep '_ready'"
|
|
94
|
+
description: "Preview _ready function and nearby code"
|
|
95
|
+
- command: "reveal player.gd -l 3 --grep 'func take_damage' -C 5"
|
|
96
|
+
description: "Show take_damage function with 5 lines context"
|
plugins/python.yaml
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Python Source File Plugin
|
|
2
|
+
# Provides 4-level progressive disclosure for .py files
|
|
3
|
+
|
|
4
|
+
extension: .py
|
|
5
|
+
name: Python Source
|
|
6
|
+
description: Python source files with AST-based analysis
|
|
7
|
+
icon: 🐍
|
|
8
|
+
|
|
9
|
+
# Hierarchical levels - each level reveals more detail
|
|
10
|
+
levels:
|
|
11
|
+
0:
|
|
12
|
+
name: metadata
|
|
13
|
+
description: File statistics and basic information
|
|
14
|
+
breadcrumb: "File metadata (size, encoding, line count)"
|
|
15
|
+
analyzer: null # Built-in metadata analyzer
|
|
16
|
+
outputs:
|
|
17
|
+
- file_size
|
|
18
|
+
- line_count
|
|
19
|
+
- encoding
|
|
20
|
+
- last_modified
|
|
21
|
+
- file_type
|
|
22
|
+
next_levels: [1, 2, 3]
|
|
23
|
+
|
|
24
|
+
1:
|
|
25
|
+
name: structure
|
|
26
|
+
description: Code structure without implementations
|
|
27
|
+
breadcrumb: "Imports, classes, functions (structure only)"
|
|
28
|
+
analyzer: python_structure
|
|
29
|
+
outputs:
|
|
30
|
+
- imports
|
|
31
|
+
- classes
|
|
32
|
+
- functions
|
|
33
|
+
- global_variables
|
|
34
|
+
- decorators
|
|
35
|
+
next_levels: [0, 2, 3]
|
|
36
|
+
tips:
|
|
37
|
+
- "Use --grep 'ClassName' to filter specific classes"
|
|
38
|
+
- "Add --context 2 to see surrounding context"
|
|
39
|
+
|
|
40
|
+
2:
|
|
41
|
+
name: preview
|
|
42
|
+
description: Signatures and docstrings without full code
|
|
43
|
+
breadcrumb: "Function signatures, docstrings, type hints"
|
|
44
|
+
analyzer: python_preview
|
|
45
|
+
outputs:
|
|
46
|
+
- function_signatures
|
|
47
|
+
- class_signatures
|
|
48
|
+
- docstrings
|
|
49
|
+
- type_hints
|
|
50
|
+
- decorators_with_args
|
|
51
|
+
next_levels: [0, 1, 3]
|
|
52
|
+
tips:
|
|
53
|
+
- "See full implementation with --level 3"
|
|
54
|
+
- "Filter by function name with --grep"
|
|
55
|
+
|
|
56
|
+
3:
|
|
57
|
+
name: full
|
|
58
|
+
description: Complete source code with paging
|
|
59
|
+
breadcrumb: "Complete file contents (paged)"
|
|
60
|
+
analyzer: null # Built-in full content reader
|
|
61
|
+
outputs:
|
|
62
|
+
- full_source
|
|
63
|
+
next_levels: [0, 1, 2]
|
|
64
|
+
paging: true
|
|
65
|
+
page_size: 120
|
|
66
|
+
tips:
|
|
67
|
+
- "Use --grep to filter lines"
|
|
68
|
+
- "Add --context N for surrounding lines"
|
|
69
|
+
|
|
70
|
+
# Features available at all levels
|
|
71
|
+
features:
|
|
72
|
+
grep: true
|
|
73
|
+
context: true
|
|
74
|
+
paging: true
|
|
75
|
+
line_numbers: true
|
|
76
|
+
syntax_highlighting: true # Future
|
|
77
|
+
|
|
78
|
+
# Analyzer configuration
|
|
79
|
+
analyzer_config:
|
|
80
|
+
include_private: false
|
|
81
|
+
include_magic: false
|
|
82
|
+
parse_docstrings: true
|
|
83
|
+
extract_type_hints: true
|
|
84
|
+
|
|
85
|
+
# Examples shown in help text
|
|
86
|
+
examples:
|
|
87
|
+
- command: "reveal app.py"
|
|
88
|
+
description: "Show file metadata (level 0)"
|
|
89
|
+
- command: "reveal app.py -l 1"
|
|
90
|
+
description: "Show code structure (imports, classes, functions)"
|
|
91
|
+
- command: "reveal app.py -l 2 --grep 'UserManager'"
|
|
92
|
+
description: "Preview UserManager class signatures"
|
|
93
|
+
- command: "reveal app.py -l 3 --grep 'def process' -C 5"
|
|
94
|
+
description: "Show process functions with 5 lines context"
|
plugins/yaml.yaml
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# YAML File Plugin
|
|
2
|
+
# Provides 4-level progressive disclosure for .yaml and .yml files
|
|
3
|
+
|
|
4
|
+
extension: [.yaml, .yml]
|
|
5
|
+
name: YAML
|
|
6
|
+
description: YAML configuration and data files
|
|
7
|
+
icon: 📝
|
|
8
|
+
|
|
9
|
+
levels:
|
|
10
|
+
0:
|
|
11
|
+
name: metadata
|
|
12
|
+
description: File statistics and YAML characteristics
|
|
13
|
+
breadcrumb: "File metadata and YAML info"
|
|
14
|
+
analyzer: null
|
|
15
|
+
outputs:
|
|
16
|
+
- file_size
|
|
17
|
+
- line_count
|
|
18
|
+
- encoding
|
|
19
|
+
- yaml_version
|
|
20
|
+
- has_anchors
|
|
21
|
+
- has_aliases
|
|
22
|
+
- document_count
|
|
23
|
+
next_levels: [1, 2, 3]
|
|
24
|
+
|
|
25
|
+
1:
|
|
26
|
+
name: structure
|
|
27
|
+
description: Top-level keys and nested structure
|
|
28
|
+
breadcrumb: "Top-level keys and nesting depth"
|
|
29
|
+
analyzer: yaml_structure
|
|
30
|
+
outputs:
|
|
31
|
+
- top_level_keys
|
|
32
|
+
- nesting_depth
|
|
33
|
+
- key_paths
|
|
34
|
+
- data_types
|
|
35
|
+
- anchors
|
|
36
|
+
- aliases
|
|
37
|
+
next_levels: [0, 2, 3]
|
|
38
|
+
tips:
|
|
39
|
+
- "Use --grep 'database' to find database config"
|
|
40
|
+
- "See full values with --level 2"
|
|
41
|
+
|
|
42
|
+
2:
|
|
43
|
+
name: preview
|
|
44
|
+
description: Keys with value summaries
|
|
45
|
+
breadcrumb: "Keys with abbreviated values"
|
|
46
|
+
analyzer: yaml_preview
|
|
47
|
+
outputs:
|
|
48
|
+
- keys_with_values
|
|
49
|
+
- value_summaries
|
|
50
|
+
- list_lengths
|
|
51
|
+
- object_keys
|
|
52
|
+
next_levels: [0, 1, 3]
|
|
53
|
+
tips:
|
|
54
|
+
- "Long values are truncated - use level 3 for full content"
|
|
55
|
+
|
|
56
|
+
3:
|
|
57
|
+
name: full
|
|
58
|
+
description: Complete YAML content
|
|
59
|
+
breadcrumb: "Complete YAML content (paged)"
|
|
60
|
+
analyzer: null
|
|
61
|
+
outputs:
|
|
62
|
+
- full_content
|
|
63
|
+
next_levels: [0, 1, 2]
|
|
64
|
+
paging: true
|
|
65
|
+
page_size: 120
|
|
66
|
+
|
|
67
|
+
features:
|
|
68
|
+
grep: true
|
|
69
|
+
context: true
|
|
70
|
+
paging: true
|
|
71
|
+
line_numbers: true
|
|
72
|
+
syntax_highlighting: true
|
|
73
|
+
|
|
74
|
+
analyzer_config:
|
|
75
|
+
max_value_preview_length: 80
|
|
76
|
+
show_types: true
|
|
77
|
+
expand_anchors: false
|
|
78
|
+
|
|
79
|
+
examples:
|
|
80
|
+
- command: "reveal config.yaml"
|
|
81
|
+
description: "Show YAML metadata"
|
|
82
|
+
- command: "reveal config.yaml -l 1"
|
|
83
|
+
description: "Show top-level structure"
|
|
84
|
+
- command: "reveal config.yaml -l 2 --grep 'database'"
|
|
85
|
+
description: "Preview database configuration"
|
|
86
|
+
- command: "reveal config.yaml -l 3"
|
|
87
|
+
description: "Show complete YAML content"
|
reveal/__init__.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Reveal - Explore code semantically.
|
|
2
|
+
|
|
3
|
+
A clean, simple tool for progressive code exploration.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
# Version is read from pyproject.toml at runtime
|
|
7
|
+
try:
|
|
8
|
+
from importlib.metadata import version
|
|
9
|
+
__version__ = version("reveal-cli")
|
|
10
|
+
except Exception:
|
|
11
|
+
# Fallback for development/editable installs
|
|
12
|
+
__version__ = "0.8.0-dev"
|
|
13
|
+
|
|
14
|
+
# Import base classes for external use
|
|
15
|
+
from .base import FileAnalyzer, register, get_analyzer
|
|
16
|
+
from .treesitter import TreeSitterAnalyzer
|
|
17
|
+
|
|
18
|
+
# Import all built-in analyzers to register them
|
|
19
|
+
from .analyzers import *
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
'FileAnalyzer',
|
|
23
|
+
'TreeSitterAnalyzer',
|
|
24
|
+
'register',
|
|
25
|
+
'get_analyzer',
|
|
26
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Built-in analyzers for reveal.
|
|
2
|
+
|
|
3
|
+
This package contains reference implementations showing how easy it is
|
|
4
|
+
to add new file type support.
|
|
5
|
+
|
|
6
|
+
Each analyzer is typically 10-20 lines of code!
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Import all analyzers to register them
|
|
10
|
+
from .python import PythonAnalyzer
|
|
11
|
+
from .rust import RustAnalyzer
|
|
12
|
+
from .go import GoAnalyzer
|
|
13
|
+
from .markdown import MarkdownAnalyzer
|
|
14
|
+
from .yaml_json import YamlAnalyzer, JsonAnalyzer
|
|
15
|
+
from .gdscript import GDScriptAnalyzer
|
|
16
|
+
from .jupyter_analyzer import JupyterAnalyzer
|
|
17
|
+
from .javascript import JavaScriptAnalyzer
|
|
18
|
+
from .typescript import TypeScriptAnalyzer
|
|
19
|
+
from .bash import BashAnalyzer
|
|
20
|
+
from .nginx import NginxAnalyzer
|
|
21
|
+
from .toml import TomlAnalyzer
|
|
22
|
+
from .dockerfile import DockerfileAnalyzer
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
'PythonAnalyzer',
|
|
26
|
+
'RustAnalyzer',
|
|
27
|
+
'GoAnalyzer',
|
|
28
|
+
'MarkdownAnalyzer',
|
|
29
|
+
'YamlAnalyzer',
|
|
30
|
+
'JsonAnalyzer',
|
|
31
|
+
'GDScriptAnalyzer',
|
|
32
|
+
'JupyterAnalyzer',
|
|
33
|
+
'JavaScriptAnalyzer',
|
|
34
|
+
'TypeScriptAnalyzer',
|
|
35
|
+
'BashAnalyzer',
|
|
36
|
+
'NginxAnalyzer',
|
|
37
|
+
'TomlAnalyzer',
|
|
38
|
+
'DockerfileAnalyzer',
|
|
39
|
+
]
|
reveal/analyzers/bash.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Bash/Shell script analyzer - tree-sitter based."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from ..base import register
|
|
5
|
+
from ..treesitter import TreeSitterAnalyzer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@register('.sh', name='Shell Script', icon='🐚')
|
|
9
|
+
@register('.bash', name='Bash Script', icon='🐚')
|
|
10
|
+
class BashAnalyzer(TreeSitterAnalyzer):
|
|
11
|
+
"""Bash/Shell script analyzer.
|
|
12
|
+
|
|
13
|
+
Full shell script support via tree-sitter!
|
|
14
|
+
Extracts:
|
|
15
|
+
- Function definitions
|
|
16
|
+
- Variable assignments
|
|
17
|
+
- Command invocations
|
|
18
|
+
|
|
19
|
+
Cross-platform compatible:
|
|
20
|
+
- Analyzes bash scripts on any OS (Windows/Linux/macOS)
|
|
21
|
+
- Does NOT execute scripts, only parses syntax
|
|
22
|
+
- Useful for DevOps/deployment script exploration
|
|
23
|
+
- Works with WSL, Git Bash, and native Unix shells
|
|
24
|
+
|
|
25
|
+
Note: This analyzes bash script SYNTAX, regardless of the host OS.
|
|
26
|
+
"""
|
|
27
|
+
language = 'bash'
|
|
28
|
+
|
|
29
|
+
def _get_function_name(self, node) -> Optional[str]:
|
|
30
|
+
"""Extract function name from bash function_definition node.
|
|
31
|
+
|
|
32
|
+
Bash tree-sitter uses 'word' for function names, not 'identifier'.
|
|
33
|
+
|
|
34
|
+
Bash function syntax:
|
|
35
|
+
- function name() { ... } # 'function' keyword, then 'word'
|
|
36
|
+
- name() { ... } # just 'word'
|
|
37
|
+
"""
|
|
38
|
+
# Look for 'word' child (bash uses this instead of 'identifier')
|
|
39
|
+
for child in node.children:
|
|
40
|
+
if child.type == 'word':
|
|
41
|
+
return self._get_node_text(child)
|
|
42
|
+
|
|
43
|
+
# Fallback to parent implementation
|
|
44
|
+
return super()._get_function_name(node)
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""Dockerfile analyzer."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import Dict, List, Any, Optional
|
|
5
|
+
from ..base import FileAnalyzer, register
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@register('Dockerfile', name='Dockerfile', icon='🐳')
|
|
9
|
+
class DockerfileAnalyzer(FileAnalyzer):
|
|
10
|
+
"""Dockerfile analyzer.
|
|
11
|
+
|
|
12
|
+
Extracts Docker directives (FROM, RUN, COPY, ENV, EXPOSE, etc.).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def get_structure(self) -> Dict[str, List[Dict[str, Any]]]:
|
|
16
|
+
"""Extract Dockerfile directives."""
|
|
17
|
+
from_images = []
|
|
18
|
+
runs = []
|
|
19
|
+
copies = []
|
|
20
|
+
envs = []
|
|
21
|
+
exposes = []
|
|
22
|
+
workdirs = []
|
|
23
|
+
entrypoints = []
|
|
24
|
+
cmds = []
|
|
25
|
+
labels = []
|
|
26
|
+
args = []
|
|
27
|
+
|
|
28
|
+
# Track multi-line continuations
|
|
29
|
+
continued_line = ""
|
|
30
|
+
continued_start = 0
|
|
31
|
+
|
|
32
|
+
for i, line in enumerate(self.lines, 1):
|
|
33
|
+
stripped = line.strip()
|
|
34
|
+
|
|
35
|
+
# Handle line continuations (\)
|
|
36
|
+
if continued_line:
|
|
37
|
+
continued_line += " " + stripped.rstrip('\\')
|
|
38
|
+
if not stripped.endswith('\\'):
|
|
39
|
+
# Process the complete continued line
|
|
40
|
+
self._process_directive(
|
|
41
|
+
continued_line, continued_start,
|
|
42
|
+
from_images, runs, copies, envs, exposes,
|
|
43
|
+
workdirs, entrypoints, cmds, labels, args
|
|
44
|
+
)
|
|
45
|
+
continued_line = ""
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
# Skip empty lines and comments
|
|
49
|
+
if not stripped or stripped.startswith('#'):
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
# Check for line continuation
|
|
53
|
+
if stripped.endswith('\\'):
|
|
54
|
+
continued_line = stripped.rstrip('\\')
|
|
55
|
+
continued_start = i
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
# Process single-line directive
|
|
59
|
+
self._process_directive(
|
|
60
|
+
stripped, i,
|
|
61
|
+
from_images, runs, copies, envs, exposes,
|
|
62
|
+
workdirs, entrypoints, cmds, labels, args
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Build result
|
|
66
|
+
result = {}
|
|
67
|
+
if from_images:
|
|
68
|
+
result['from'] = from_images
|
|
69
|
+
if runs:
|
|
70
|
+
result['run'] = runs
|
|
71
|
+
if copies:
|
|
72
|
+
result['copy'] = copies
|
|
73
|
+
if envs:
|
|
74
|
+
result['env'] = envs
|
|
75
|
+
if exposes:
|
|
76
|
+
result['expose'] = exposes
|
|
77
|
+
if workdirs:
|
|
78
|
+
result['workdir'] = workdirs
|
|
79
|
+
if entrypoints:
|
|
80
|
+
result['entrypoint'] = entrypoints
|
|
81
|
+
if cmds:
|
|
82
|
+
result['cmd'] = cmds
|
|
83
|
+
if labels:
|
|
84
|
+
result['label'] = labels
|
|
85
|
+
if args:
|
|
86
|
+
result['arg'] = args
|
|
87
|
+
|
|
88
|
+
return result
|
|
89
|
+
|
|
90
|
+
def _process_directive(self, line: str, line_num: int,
|
|
91
|
+
from_images, runs, copies, envs, exposes,
|
|
92
|
+
workdirs, entrypoints, cmds, labels, args):
|
|
93
|
+
"""Process a single Dockerfile directive."""
|
|
94
|
+
# Match directive at start of line
|
|
95
|
+
directive_match = re.match(r'^([A-Z]+)\s+(.+)$', line)
|
|
96
|
+
if not directive_match:
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
directive = directive_match.group(1)
|
|
100
|
+
args_str = directive_match.group(2).strip()
|
|
101
|
+
|
|
102
|
+
if directive == 'FROM':
|
|
103
|
+
# Extract base image
|
|
104
|
+
from_images.append({
|
|
105
|
+
'line': line_num,
|
|
106
|
+
'name': args_str,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
elif directive == 'RUN':
|
|
110
|
+
# Truncate long commands
|
|
111
|
+
display = args_str[:80] + '...' if len(args_str) > 80 else args_str
|
|
112
|
+
runs.append({
|
|
113
|
+
'line': line_num,
|
|
114
|
+
'content': display,
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
elif directive in ['COPY', 'ADD']:
|
|
118
|
+
# Extract source -> dest
|
|
119
|
+
copies.append({
|
|
120
|
+
'line': line_num,
|
|
121
|
+
'content': args_str,
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
elif directive == 'ENV':
|
|
125
|
+
# Extract environment variable
|
|
126
|
+
envs.append({
|
|
127
|
+
'line': line_num,
|
|
128
|
+
'content': args_str,
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
elif directive == 'EXPOSE':
|
|
132
|
+
# Extract port
|
|
133
|
+
exposes.append({
|
|
134
|
+
'line': line_num,
|
|
135
|
+
'content': args_str,
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
elif directive == 'WORKDIR':
|
|
139
|
+
workdirs.append({
|
|
140
|
+
'line': line_num,
|
|
141
|
+
'content': args_str,
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
elif directive == 'ENTRYPOINT':
|
|
145
|
+
entrypoints.append({
|
|
146
|
+
'line': line_num,
|
|
147
|
+
'content': args_str,
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
elif directive == 'CMD':
|
|
151
|
+
cmds.append({
|
|
152
|
+
'line': line_num,
|
|
153
|
+
'content': args_str,
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
elif directive == 'LABEL':
|
|
157
|
+
labels.append({
|
|
158
|
+
'line': line_num,
|
|
159
|
+
'content': args_str,
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
elif directive == 'ARG':
|
|
163
|
+
args.append({
|
|
164
|
+
'line': line_num,
|
|
165
|
+
'content': args_str,
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
def extract_element(self, element_type: str, name: str) -> Optional[Dict[str, Any]]:
|
|
169
|
+
"""Extract a specific directive or stage.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
element_type: 'from', 'run', etc.
|
|
173
|
+
name: Search term
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Dict with directive content
|
|
177
|
+
"""
|
|
178
|
+
# Fall back to grep-based search
|
|
179
|
+
return super().extract_element(element_type, name)
|