Skip to content

Commit 40ae2ee

Browse files
authored
Merge pull request #128 from spicecode-cli/arrumei_tudo
2 parents 3a41567 + 7615867 commit 40ae2ee

File tree

8 files changed

+256
-81
lines changed

8 files changed

+256
-81
lines changed

CODE_OF_CONDUCT.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,64 @@
11
# The Spice Code of Conduct
22

33
## **The Great Pledge**
4+
45
As Fremen of this digital desert, we pledge to make our oasis of code a safe and welcoming place for all. Regardless of background, experience, or identity, all who walk the sands of Spice Code CLI shall find respect and camaraderie.
56

67
We shall act with honor, uphold fairness, and protect our community from the corruption of toxicity, so that our shared journey may be one of learning and mutual growth.
78

89
## **The Way of the Spice**
10+
911
To ensure our collective strength, we adhere to these principles:
1012

1113
### **What is Expected of All Fremen**
14+
1215
- Show kindness, patience, and respect to fellow travelers.
1316
- Honor differences in coding styles, ideas, and perspectives.
1417
- Provide constructive feedback with humility and receive it with grace.
1518
- Accept responsibility for mistakes and strive to improve.
1619
- Work for the greater good of the community, not just personal gain.
1720

1821
### **That Which is Forbidden**
22+
1923
- Harassment, discrimination, or hostility in any form.
2024
- Trolling, insults, or personal attacks—only the weak resort to such things.
2125
- Sharing private information without consent, for such betrayals are not easily forgiven.
2226
- Any conduct unbecoming of a Fremen coder that disrupts the harmony of our sietch.
2327

2428
## **The Law of the Desert**
29+
2530
The stewards of Spice Code CLI (maintainers and community leaders) shall act as judges in matters of discord. They have the right to remove, edit, or reject any contribution that violates our principles.
2631

2732
## **The Fremen Enforcement Measures**
33+
2834
Those who stray from the path shall be met with fair but firm consequences:
2935

3036
### **1. A Whisper on the Wind**
37+
3138
_Impact_: A minor breach—unintended offense or misunderstanding.
3239
_Consequence_: A private warning and guidance toward the proper path.
3340

3441
### **2. The Warning of the Sietch**
42+
3543
_Impact_: A more serious offense or repeated minor offenses.
3644
_Consequence_: A public warning with temporary restrictions on participation.
3745

3846
### **3. Exile from the Oasis**
47+
3948
_Impact_: Disruptive or harmful behavior that endangers the community.
4049
_Consequence_: A temporary ban from Spice Code CLI spaces.
4150

4251
### **4. Cast Into the Deep Desert**
52+
4353
_Impact_: Malicious intent, harassment, or repeated major offenses.
4454
_Consequence_: Permanent banishment—no spice, no code, no return.
4555

4656
## **Reporting a Violation**
57+
4758
If you witness behavior unworthy of the Spice Code, report it to the stewards at spicecodecli@gmail.com. Reports will be handled swiftly, fairly, and with the utmost discretion.
4859

4960
## **Final Words**
61+
5062
Spice Code CLI exists to empower developers. Let us build with respect, learn from one another, and ensure our community thrives. The spice must flow, but toxicity shall not.
5163

5264
**“He who controls the code, controls the future.”**

cli/commands/analyze.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,20 @@ def analyze_command(file, all, json_output, LANG_FILE):
1212
# load translations
1313
messages = get_translation(LANG_FILE)
1414

15-
# define available stats UPDATE THIS WHEN NEEDED PLEASE !!!!!!!!
15+
# define available stats
1616
available_stats = [
1717
"line_count",
1818
"function_count",
19-
"comment_line_count"
19+
"comment_line_count",
20+
"indentation_level"
2021
]
2122

22-
# dictionary for the stats UPDATE THIS WHEN NEEDED PLEASE !!!!!!!!
23+
# dictionary for the stats
2324
stats_labels = {
2425
"line_count": messages.get("line_count_option", "Line Count"),
2526
"function_count": messages.get("function_count_option", "Function Count"),
26-
"comment_line_count": messages.get("comment_line_count_option", "Comment Line Count")
27+
"comment_line_count": messages.get("comment_line_count_option", "Comment Line Count"),
28+
"indentation_level": messages.get("indentation_level_option", "Indentation Analysis")
2729
}
2830

2931
# If --all flag is used, skip the selection menu and use all stats
@@ -62,7 +64,7 @@ def analyze_command(file, all, json_output, LANG_FILE):
6264
try:
6365
# show analyzing message if not in JSON mode
6466
if not json_output:
65-
print(f"{messages['analyzing_file']}: {file}")
67+
print(f"{messages.get('analyzing_file', 'Analyzing file')}: {file}")
6668

6769
# get analysis results from analyze_file
6870
results = analyze_file(file, selected_stats=selected_stat_keys)
@@ -74,8 +76,11 @@ def analyze_command(file, all, json_output, LANG_FILE):
7476
else:
7577
# only print the selected stats in normal mode
7678
for stat in selected_stat_keys:
77-
if stat in results:
78-
print(messages[stat].format(count=results[stat]))
79+
if stat == "indentation_level" and "indentation_type" in results:
80+
print(f"{messages.get('indentation_type', 'Indentation Type')}: {results['indentation_type']}")
81+
print(f"{messages.get('indentation_size', 'Indentation Size')}: {results['indentation_size']}")
82+
elif stat in results:
83+
print(messages.get(stat, f"{stat.replace('_', ' ').title()}: {{count}}").format(count=results[stat]))
7984

8085
except Exception as e:
8186
if json_output:
@@ -84,5 +89,4 @@ def analyze_command(file, all, json_output, LANG_FILE):
8489
error_msg = str(e).replace('\n', ' ')
8590
print(json.dumps({"error": error_msg}))
8691
else:
87-
print(f"[red]{messages['error']}[/] {e}")
88-
92+
print(f"{messages.get('error', 'Error')}: {e}")

cli/commands/export/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""
2+
Módulo de inicialização para o pacote de exportação.
3+
"""

cli/commands/export/export.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import os
2+
import json
3+
import csv
4+
import typer
5+
from rich.console import Console
6+
from rich.table import Table
7+
8+
from cli.utils.get_translation import get_translation
9+
10+
def export_results(results, format_type, output_file, messages):
11+
"""
12+
Export analysis results to a file in the specified format.
13+
14+
Args:
15+
results (dict): Analysis results to export
16+
format_type (str): Format to export (json, csv, html, markdown)
17+
output_file (str): Path to output file
18+
messages (dict): Translation messages
19+
20+
Returns:
21+
bool: True if export was successful, False otherwise
22+
"""
23+
try:
24+
# Create directory if it doesn't exist
25+
os.makedirs(os.path.dirname(os.path.abspath(output_file)), exist_ok=True)
26+
27+
if format_type == "json":
28+
with open(output_file, "w", encoding="utf-8") as f:
29+
json.dump(results, f, indent=2)
30+
31+
elif format_type == "csv":
32+
with open(output_file, "w", encoding="utf-8", newline="") as f:
33+
writer = csv.writer(f)
34+
# Write header
35+
writer.writerow(["Metric", "Value"])
36+
# Write data
37+
for key, value in results.items():
38+
if isinstance(value, (int, float, str)):
39+
writer.writerow([key, value])
40+
elif isinstance(value, list):
41+
writer.writerow([key, json.dumps(value)])
42+
43+
elif format_type == "markdown":
44+
with open(output_file, "w", encoding="utf-8") as f:
45+
f.write(f"# {messages.get('analysis_results', 'Analysis Results')}\n\n")
46+
f.write(f"**{messages.get('file_name', 'File')}: {results.get('file_name', 'Unknown')}**\n\n")
47+
f.write("| Metric | Value |\n")
48+
f.write("|--------|-------|\n")
49+
for key, value in results.items():
50+
if isinstance(value, (int, float, str)):
51+
f.write(f"| {key.replace('_', ' ').title()} | {value} |\n")
52+
elif isinstance(value, list) and key == "indentation_levels":
53+
f.write(f"| {key.replace('_', ' ').title()} | {len(value)} levels |\n")
54+
55+
elif format_type == "html":
56+
with open(output_file, "w", encoding="utf-8") as f:
57+
f.write("<!DOCTYPE html>\n<html>\n<head>\n")
58+
f.write("<meta charset=\"utf-8\">\n")
59+
f.write("<title>SpiceCode Analysis Results</title>\n")
60+
f.write("<style>\n")
61+
f.write("body { font-family: Arial, sans-serif; margin: 20px; }\n")
62+
f.write("table { border-collapse: collapse; width: 100%; }\n")
63+
f.write("th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }\n")
64+
f.write("th { background-color: #f2f2f2; }\n")
65+
f.write("h1 { color: #333; }\n")
66+
f.write("</style>\n</head>\n<body>\n")
67+
f.write(f"<h1>{messages.get('analysis_results', 'Analysis Results')}</h1>\n")
68+
f.write(f"<p><strong>{messages.get('file_name', 'File')}: {results.get('file_name', 'Unknown')}</strong></p>\n")
69+
f.write("<table>\n<tr><th>Metric</th><th>Value</th></tr>\n")
70+
for key, value in results.items():
71+
if isinstance(value, (int, float, str)):
72+
f.write(f"<tr><td>{key.replace('_', ' ').title()}</td><td>{value}</td></tr>\n")
73+
elif isinstance(value, list) and key == "indentation_levels":
74+
f.write(f"<tr><td>{key.replace('_', ' ').title()}</td><td>{len(value)} levels</td></tr>\n")
75+
f.write("</table>\n</body>\n</html>")
76+
77+
else:
78+
return False
79+
80+
return True
81+
82+
except Exception as e:
83+
print(f"{messages.get('export_error', 'Export error')}: {str(e)}")
84+
return False
85+
86+
def export_command(file, format_type, output, LANG_FILE):
87+
"""
88+
Export analysis results to a file.
89+
"""
90+
# Load translations
91+
messages = get_translation(LANG_FILE)
92+
console = Console()
93+
94+
# Validate format type
95+
valid_formats = ["json", "csv", "markdown", "html"]
96+
if format_type not in valid_formats:
97+
console.print(f"[red]{messages.get('invalid_format', 'Invalid format')}[/] {format_type}")
98+
console.print(f"{messages.get('valid_formats', 'Valid formats')}: {', '.join(valid_formats)}")
99+
return
100+
101+
try:
102+
# Analyze file
103+
from spice.analyze import analyze_file
104+
results = analyze_file(file)
105+
106+
# Set default output file if not provided
107+
if not output:
108+
base_name = os.path.splitext(os.path.basename(file))[0]
109+
output = f"{base_name}_analysis.{format_type}"
110+
111+
# Export results
112+
success = export_results(results, format_type, output, messages)
113+
114+
if success:
115+
console.print(f"[green]{messages.get('export_success', 'Export successful')}[/]: {output}")
116+
else:
117+
console.print(f"[red]{messages.get('export_failed', 'Export failed')}[/]")
118+
119+
except Exception as e:
120+
console.print(f"[red]{messages.get('error', 'Error')}[/]: {str(e)}")

cli/main.py

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from cli.commands.hello import hello_command
88
from cli.commands.version import version_command
99
from cli.commands.analyze import analyze_command
10-
10+
from cli.commands.export.export import export_command
1111

1212
# initialize typer
1313
app = typer.Typer()
@@ -18,48 +18,30 @@
1818
# get current directory, this is needed for it to work on other peoples computers via pip
1919
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
2020

21-
# select a file to save the current selected langague (if saved to memory it wont persist between commands)
21+
# select a file to save the current selected language
2222
LANG_FILE = os.path.join(CURRENT_DIR, "lang.txt")
2323

24-
25-
26-
# SPICE TRANSLATE COMMAND
2724
@app.command()
2825
def translate():
2926
"""
3027
Set the language for CLI messages.
3128
"""
32-
3329
translate_command(LANG_FILE)
3430

35-
# -- end -- #
36-
37-
38-
# SPICE HELLO COMMAND
3931
@app.command()
4032
def hello():
4133
"""
4234
Welcome message.
4335
"""
44-
4536
hello_command(LANG_FILE)
4637

47-
# -- end -- #
48-
49-
50-
# SPICE VERSION COMMAND
5138
@app.command()
5239
def version():
5340
"""
5441
Display the current version of the application.
5542
"""
56-
5743
version_command(LANG_FILE, CURRENT_DIR)
5844

59-
#--- end ---#
60-
61-
62-
# SPICE ANALYZE COMMAND
6345
@app.command()
6446
def analyze(
6547
file: str,
@@ -69,20 +51,21 @@ def analyze(
6951
"""
7052
Analyze the given file.
7153
"""
72-
7354
analyze_command(file, all, json_output, LANG_FILE)
7455

75-
# -- end -- #
76-
56+
@app.command()
57+
def export(
58+
file: str,
59+
format_type: str = typer.Option("json", "--format", "-f", help="Export format (json, csv, markdown, html)"),
60+
output: str = typer.Option(None, "--output", "-o", help="Output file path")
61+
):
62+
"""
63+
Analyze a file and export results to a file in the specified format.
64+
"""
65+
export_command(file, format_type, output, LANG_FILE)
7766

7867
def main():
7968
app() # run typer
8069

81-
# -- end -- #
82-
83-
84-
# whatever the fuck this is python makes no sense
8570
if __name__ == "__main__":
8671
main()
87-
88-
# -- end -- #

lexers/golang/golexer.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,21 @@ def tokenize_string(self):
186186
elif char == "\\": # escape de caracteres
187187
self.position += 1
188188
self.column += 1
189+
if self.position < len(self.source_code): # avança o caractere escapado
190+
self.position += 1
191+
self.column += 1
192+
continue
193+
elif char == "\n": # se tiver uma nova linha dentro da string
194+
# Em Go, strings com aspas duplas não podem conter quebras de linha
195+
# mas strings com crases (raw strings) podem
196+
if quote_char == '"':
197+
return Token(TokenType.ERROR, "string não fechada", self.line, start_col)
198+
self.line += 1
199+
self.column = 1
200+
self.current_line_start = self.position + 1
201+
else:
202+
self.column += 1
189203
self.position += 1
190-
self.column += 1
191204

192205
string_value = self.source_code[start_pos:self.position] # pega o texto da string
193206
return Token(TokenType.STRING, string_value, self.line, start_col)
@@ -210,9 +223,9 @@ def tokenize_identifier(self):
210223
def match_operator(self):
211224
"""tenta casar com um operador."""
212225
for op in sorted(self.OPERATORS, key=len, reverse=True): # verifica operadores mais longos primeiro
213-
if self.source_code.startswith(op, self.position):
226+
if self.position + len(op) <= len(self.source_code) and self.source_code[self.position:self.position + len(op)] == op:
214227
token = Token(TokenType.OPERATOR, op, self.line, self.column)
215228
self.position += len(op)
216229
self.column += len(op)
217230
return token
218-
return None
231+
return None

0 commit comments

Comments
 (0)