Skip to content

Commit b35424f

Browse files
authored
Merge pull request #147 from spicecodecli/n2_gustavo
N2
2 parents 1fbb59d + 5b3e835 commit b35424f

15 files changed

Lines changed: 247 additions & 53 deletions

cli/commands/analyze.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ def analyze_command(file, all, json_output, LANG_FILE):
1818
"function_count",
1919
"comment_line_count",
2020
"inline_comment_count",
21-
"indentation_level"
21+
"indentation_level",
22+
"external_dependencies_count",
23+
"method_type_count",
2224
]
2325

2426
# dictionary for the stats
@@ -27,7 +29,11 @@ def analyze_command(file, all, json_output, LANG_FILE):
2729
"function_count": messages.get("function_count_option", "Function Count"),
2830
"comment_line_count": messages.get("comment_line_count_option", "Comment Line Count"),
2931
"inline_comment_count": messages.get("inline_comment_count_option", "Inline Comment Count"),
30-
"indentation_level": messages.get("indentation_level_option", "Indentation Analysis")
32+
"indentation_level": messages.get("indentation_level_option", "Indentation Analysis"),
33+
"external_dependencies_count": messages.get("external_dependencies_count_option", "External Dependencies Count"),
34+
"method_type_count": messages.get("methods_count_option", "Method Type Count"),
35+
"private_methods_count": messages.get("private_methods_count_option", "Private Methods Count"),
36+
"public_methods_count": messages.get("public_methods_count_option", "Public Methods Count"),
3137
}
3238

3339
# If --all flag is used, skip the selection menu and use all stats
@@ -78,12 +84,20 @@ def analyze_command(file, all, json_output, LANG_FILE):
7884
else:
7985
# only print the selected stats in normal mode
8086
for stat in selected_stat_keys:
87+
#print(stat) #debug
88+
if stat == "method_type_count" and "method_type_count" in results:
89+
mtc = results["method_type_count"]
90+
print(f"{messages.get('public_methods_count_option', 'Public Methods Count')}: {mtc['public']}")
91+
print(f"{messages.get('private_methods_count_option', 'Private Methods Count')}: {mtc['private']}")
92+
continue
93+
8194
if stat == "indentation_level" and "indentation_type" in results:
82-
print(f"{messages.get('indentation_type', 'Indentation Type')}: {results['indentation_type']}")
83-
print(f"{messages.get('indentation_size', 'Indentation Size')}: {results['indentation_size']}")
95+
print(f"{messages.get('indentation_type', 'Indentation Type')}: {results.get('indentation_type', 'N/A')}")
96+
print(f"{messages.get('indentation_size', 'Indentation Size')}: {results.get('indentation_size', 'N/A')}")
97+
continue
98+
8499
elif stat in results:
85-
print(messages.get(stat, f"{stat.replace('_', ' ').title()}: {{count}}").format(count=results[stat]))
86-
100+
print(f"{stats_labels[stat]}: {results[stat]}")
87101
except Exception as e:
88102
if json_output:
89103
import json

cli/translations/en.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,12 @@
2222
# keys for the version command
2323
"version_info": "SpiceCode Version:",
2424
"version_not_found": "Version information not found in setup.py",
25-
"setup_not_found": "Error: setup.py not found."
25+
"setup_not_found": "Error: setup.py not found.",
26+
"indentation_level_option": "Indentation Level Analysis",
27+
"indentation_type": "Indentation Type",
28+
"indentation_size": "Indentation Size",
29+
"external_dependencies_count_option": "External Dependency Count",
30+
"methods_count_option": "Method Type Count",
31+
"private_methods_count_option": "Private Methods Count",
32+
"public_methods_count_option": "Public Methods Count",
2633
}

cli/translations/fremen.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,12 @@
1818
"inline_comment_count_option": "Passages of Dual Meaning",
1919
"no_stats_selected": "No omens were heeded. The analysis fades into the sands.",
2020
"confirm_and_analyze": "Seal your fate and analyze",
21-
"checkbox_hint": "(Use space to mark, enter to proceed)"
21+
"checkbox_hint": "(Use space to mark, enter to proceed)",
22+
"indentation_level_option": "Sand Pattern Reading",
23+
"indentation_type": "Kind of Sand Grain",
24+
"indentation_size": "Size of the Sand Grain",
25+
"external_dependencies_count_option": "Grains of Sand Beyound Dune",
26+
"methods_count_option": "Kinds of Rituals",
27+
"private_methods_count_option": "Hidden Rituals",
28+
"public_methods_count_option": "Open Rituals",
2229
}

cli/translations/pt-br.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,12 @@
1818
"inline_comment_count_option": "Contagem de Comentários Inline",
1919
"no_stats_selected": "Nenhuma estatística selecionada. Análise cancelada.",
2020
"confirm_and_analyze": "Confirmar e analisar",
21-
"checkbox_hint": "(Use espaço para selecionar, enter para confirmar)"
21+
"checkbox_hint": "(Use espaço para selecionar, enter para confirmar)",
22+
"indentation_level_option": "Análise de Indentação",
23+
"indentation_type": "Tipo de Indentação",
24+
"indentation_size": "Tamanho da Indentação",
25+
"external_dependencies_count_option": "Contagem de Dependências Externas",
26+
"methods_count_option": "Contagem de Tipos de Métodos",
27+
"private_methods_count_option": "Contagem de Métodos Privados",
28+
"public_methods_count_option": "Contagem de Métodos Públicos",
2229
}

spice/analyze.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os
22
from typing import List, Dict, Optional, Union
33

4-
from spice.analyzers.identation import detect_indentation
4+
from spice.analyzers.indentation import detect_indentation
55

66
def analyze_file(file_path: str, selected_stats: Optional[List[str]] = None) -> Dict[str, Union[int, str, List[int]]]:
77
"""
@@ -35,7 +35,7 @@ def analyze_file(file_path: str, selected_stats: Optional[List[str]] = None) ->
3535
raise ValueError("File has no extension")
3636

3737
# Define valid stats
38-
valid_stats = ["line_count", "function_count", "comment_line_count", "inline_comment_count", "indentation_level"]
38+
valid_stats = ["line_count", "function_count", "comment_line_count", "inline_comment_count", "indentation_level", "external_dependencies_count", "method_type_count"]
3939

4040
# default to all stats if none specified
4141
if selected_stats is None:
@@ -82,19 +82,29 @@ def analyze_file(file_path: str, selected_stats: Optional[List[str]] = None) ->
8282

8383
# indentation analysis if requested
8484
if "indentation_level" in selected_stats:
85-
indentation_info = detect_indentation(code)
86-
results["indentation_type"] = indentation_info["indent_type"]
87-
results["indentation_size"] = indentation_info["indent_size"]
88-
results["indentation_levels"] = indentation_info["levels"]
89-
85+
from spice.analyzers.indentation import detect_indentation
86+
indentation_info = detect_indentation(file_path)
87+
results["indentation_type"] = indentation_info["indentation_type"]
88+
results["indentation_size"] = indentation_info["indentation_size"]
89+
9090
# function count if requested
9191
if "function_count" in selected_stats:
9292
from spice.analyzers.count_functions import count_functions
93-
from utils.get_lexer import get_lexer_for_file
94-
LexerClass = get_lexer_for_file(file_path)
95-
lexer = LexerClass(source_code=code) # Pass source_code explicitly
9693
results["function_count"] = count_functions(file_path)
9794

95+
# external dependencies count if requested
96+
if "external_dependencies_count" in selected_stats:
97+
from spice.analyzers.count_external_dependencies import count_external_dependencies
98+
results["external_dependencies_count"] = count_external_dependencies(file_path)
99+
100+
# method type count if requested
101+
if "method_type_count" in selected_stats:
102+
from spice.analyzers.count_method_type import count_method_type
103+
private_methods, public_methods = count_method_type(file_path)
104+
results["method_type_count"] = {
105+
"private": private_methods,
106+
"public": public_methods
107+
}
98108
return results
99109

100110
except Exception as e:
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import os
2+
import re
3+
4+
def count_external_dependencies(path):
5+
"""Contar o número de dependências externas em um arquivo de exemplo."""
6+
_, ext = os.path.splitext(path)
7+
with open(path, 'r') as file:
8+
code = file.read()
9+
10+
if ext == '.py':
11+
# Contar o número de importações
12+
pattern = r'^\s*(import\s+\w+|from\s+\w+\s+import\s+.+)'
13+
matches = re.findall(pattern, code, flags=re.MULTILINE)
14+
return len(matches)
15+
elif ext == '.js':
16+
# Contar o número de importações
17+
require_pattern = r'require\s*\(\s*[\'"][^\'"]+[\'"]\s*\)'
18+
import_pattern = r'^\s*import\s+.*from\s+[\'"][^\'"]+[\'"]'
19+
matches = re.findall(require_pattern, code)
20+
matches += re.findall(import_pattern, code, flags=re.MULTILINE)
21+
return len(matches)
22+
elif ext == ".rb":
23+
pattern = r'^\s*require(_relative)?\s+[\'"][^\'"]+[\'"]'
24+
matches = re.findall(pattern, code, flags=re.MULTILINE)
25+
return len(matches)
26+
elif ext == ".go":
27+
import_block_pattern = r'import\s*\((.*?)\)'
28+
single_import_pattern = r'^\s*import\s+[\'"][^\'"]+[\'"]'
29+
block = re.findall(import_block_pattern, code, flags=re.DOTALL)
30+
count = 0
31+
for b in block:
32+
count += len(re.findall(r'[\'"][^\'"]+[\'"]', b))
33+
count += len(re.findall(single_import_pattern, code, flags=re.MULTILINE))
34+
return count
35+
else:
36+
return 0
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import os
2+
import re
3+
4+
def count_method_type(path):
5+
"""Count the number of private and public methods in a sample file."""
6+
_, ext = os.path.splitext(path)
7+
with open(path, 'r') as file:
8+
code = file.read()
9+
10+
if ext == '.py':
11+
# Count the number of private and public methods
12+
pattern = r'^\s*def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(.*?\)\s*:\s*(?:#.*)?$'
13+
matches = re.findall(pattern, code, flags=re.MULTILINE)
14+
private_methods = [m for m in matches if m.startswith('_')]
15+
public_methods = [m for m in matches if not m.startswith('_')]
16+
return len(private_methods), len(public_methods)
17+
elif ext == '.js':
18+
# Count the number of private and public methods
19+
pattern = r'^\s*function\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(.*?\)\s*{'
20+
matches = re.findall(pattern, code, flags=re.MULTILINE)
21+
private_methods = [m for m in matches if m.startswith('_')]
22+
public_methods = [m for m in matches if not m.startswith('_')]
23+
return len(private_methods), len(public_methods)
24+
elif ext == ".rb":
25+
# Count the number of private and public methods
26+
pattern = r'^\s*def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:#.*)?$'
27+
matches = re.findall(pattern, code, flags=re.MULTILINE)
28+
private_methods = [m for m in matches if m.startswith('_')]
29+
public_methods = [m for m in matches if not m.startswith('_')]
30+
return len(private_methods), len(public_methods)
31+
elif ext == ".go":
32+
# Count the number of private and public methods
33+
pattern = r'^\s*func\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(.*?\)\s*{'
34+
matches = re.findall(pattern, code, flags=re.MULTILINE)
35+
private_methods = [m for m in matches if m[0].islower()]
36+
public_methods = [m for m in matches if m[0].isupper()]
37+
return len(private_methods), len(public_methods)
38+
else:
39+
return 0, 0

spice/analyzers/identation.py

Lines changed: 0 additions & 30 deletions
This file was deleted.

spice/analyzers/indentation.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from collections import Counter
2+
3+
def detect_indentation(file_path):
4+
"""
5+
Analyze the indentation type (spaces, tabs, or mixed) and size in a file.
6+
Returns a dict: {"indentation_type": "spaces"|"tabs"|"mixed"|"unknown", "indentation_size": int}
7+
"""
8+
indent_types = []
9+
indent_sizes = []
10+
11+
with open(file_path, "r", encoding="utf-8") as f:
12+
for line in f:
13+
if not line.strip():
14+
continue # skip empty lines
15+
leading_ws = line[:len(line) - len(line.lstrip())]
16+
if not leading_ws:
17+
continue
18+
if set(leading_ws) == {" "}:
19+
indent_types.append("spaces")
20+
indent_sizes.append(len(leading_ws))
21+
elif set(leading_ws) == {"\t"}:
22+
indent_types.append("tabs")
23+
indent_sizes.append(len(leading_ws))
24+
else:
25+
indent_types.append("mixed")
26+
indent_sizes.append(len(leading_ws))
27+
28+
if not indent_types:
29+
return {"indentation_type": "unknown", "indentation_size": 0}
30+
31+
type_counter = Counter(indent_types)
32+
if "spaces" in type_counter or "tabs" in type_counter:
33+
main_type = "spaces" if type_counter["spaces"] >= type_counter["tabs"] else "tabs"
34+
else:
35+
main_type = "mixed"
36+
37+
size_counter = Counter(
38+
size for t, size in zip(indent_types, indent_sizes) if t == main_type
39+
)
40+
main_size = size_counter.most_common(1)[0][0] if size_counter else 0
41+
42+
return {"indentation_type": main_type, "indentation_size": main_size}

tests/analyze/test_analyze_json_javascript.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ def test_analyze_command_with_json_flag():
2929

3030
# Verify the values match expected results
3131
assert output["file_name"] == os.path.basename(SAMPLE_FILE_PATH)
32-
assert output["line_count"] == 153
33-
assert output["comment_line_count"] == 21
32+
assert output["line_count"] == 172 #THIS CAN'T BE HARD CODED, BUT WE'LL FIX THIS LATER
33+
assert output["comment_line_count"] == 23 #this is the number of comment lines in the sample file
3434
assert output["function_count"] == 18
3535
assert output["inline_comment_count"] == 2
3636

@@ -46,8 +46,8 @@ def test_analyze_command_with_all_and_json_flags():
4646
output = json.loads(result.stdout)
4747

4848
# Verify the values match expected results
49-
assert output["line_count"] == 153
50-
assert output["comment_line_count"] == 21
49+
assert output["line_count"] == 172 #THIS CAN'T BE HARD CODED, BUT WE'LL FIX THIS LATER
50+
assert output["comment_line_count"] == 23
5151
assert output["function_count"] == 18
5252
assert output["inline_comment_count"] == 2
5353

0 commit comments

Comments
 (0)