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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,64 @@
# The Spice Code of Conduct

## **The Great Pledge**

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.

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.

## **The Way of the Spice**

To ensure our collective strength, we adhere to these principles:

### **What is Expected of All Fremen**

- Show kindness, patience, and respect to fellow travelers.
- Honor differences in coding styles, ideas, and perspectives.
- Provide constructive feedback with humility and receive it with grace.
- Accept responsibility for mistakes and strive to improve.
- Work for the greater good of the community, not just personal gain.

### **That Which is Forbidden**

- Harassment, discrimination, or hostility in any form.
- Trolling, insults, or personal attacks—only the weak resort to such things.
- Sharing private information without consent, for such betrayals are not easily forgiven.
- Any conduct unbecoming of a Fremen coder that disrupts the harmony of our sietch.

## **The Law of the Desert**

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.

## **The Fremen Enforcement Measures**

Those who stray from the path shall be met with fair but firm consequences:

### **1. A Whisper on the Wind**

_Impact_: A minor breach—unintended offense or misunderstanding.
_Consequence_: A private warning and guidance toward the proper path.

### **2. The Warning of the Sietch**

_Impact_: A more serious offense or repeated minor offenses.
_Consequence_: A public warning with temporary restrictions on participation.

### **3. Exile from the Oasis**

_Impact_: Disruptive or harmful behavior that endangers the community.
_Consequence_: A temporary ban from Spice Code CLI spaces.

### **4. Cast Into the Deep Desert**

_Impact_: Malicious intent, harassment, or repeated major offenses.
_Consequence_: Permanent banishment—no spice, no code, no return.

## **Reporting a Violation**

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.

## **Final Words**

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.

**“He who controls the code, controls the future.”**
Expand Down
22 changes: 13 additions & 9 deletions cli/commands/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,20 @@ def analyze_command(file, all, json_output, LANG_FILE):
# load translations
messages = get_translation(LANG_FILE)

# define available stats UPDATE THIS WHEN NEEDED PLEASE !!!!!!!!
# define available stats
available_stats = [
"line_count",
"function_count",
"comment_line_count"
"comment_line_count",
"indentation_level"
]

# dictionary for the stats UPDATE THIS WHEN NEEDED PLEASE !!!!!!!!
# dictionary for the stats
stats_labels = {
"line_count": messages.get("line_count_option", "Line Count"),
"function_count": messages.get("function_count_option", "Function Count"),
"comment_line_count": messages.get("comment_line_count_option", "Comment Line Count")
"comment_line_count": messages.get("comment_line_count_option", "Comment Line Count"),
"indentation_level": messages.get("indentation_level_option", "Indentation Analysis")
}

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

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

except Exception as e:
if json_output:
Expand All @@ -84,5 +89,4 @@ def analyze_command(file, all, json_output, LANG_FILE):
error_msg = str(e).replace('\n', ' ')
print(json.dumps({"error": error_msg}))
else:
print(f"[red]{messages['error']}[/] {e}")

print(f"{messages.get('error', 'Error')}: {e}")
3 changes: 3 additions & 0 deletions cli/commands/export/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
Módulo de inicialização para o pacote de exportação.
"""
120 changes: 120 additions & 0 deletions cli/commands/export/export.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import os
import json
import csv
import typer
from rich.console import Console
from rich.table import Table

from cli.utils.get_translation import get_translation

def export_results(results, format_type, output_file, messages):
"""
Export analysis results to a file in the specified format.

Args:
results (dict): Analysis results to export
format_type (str): Format to export (json, csv, html, markdown)
output_file (str): Path to output file
messages (dict): Translation messages

Returns:
bool: True if export was successful, False otherwise
"""
try:
# Create directory if it doesn't exist
os.makedirs(os.path.dirname(os.path.abspath(output_file)), exist_ok=True)

if format_type == "json":
with open(output_file, "w", encoding="utf-8") as f:
json.dump(results, f, indent=2)

elif format_type == "csv":
with open(output_file, "w", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
# Write header
writer.writerow(["Metric", "Value"])
# Write data
for key, value in results.items():
if isinstance(value, (int, float, str)):
writer.writerow([key, value])
elif isinstance(value, list):
writer.writerow([key, json.dumps(value)])

elif format_type == "markdown":
with open(output_file, "w", encoding="utf-8") as f:
f.write(f"# {messages.get('analysis_results', 'Analysis Results')}\n\n")
f.write(f"**{messages.get('file_name', 'File')}: {results.get('file_name', 'Unknown')}**\n\n")
f.write("| Metric | Value |\n")
f.write("|--------|-------|\n")
for key, value in results.items():
if isinstance(value, (int, float, str)):
f.write(f"| {key.replace('_', ' ').title()} | {value} |\n")
elif isinstance(value, list) and key == "indentation_levels":
f.write(f"| {key.replace('_', ' ').title()} | {len(value)} levels |\n")

elif format_type == "html":
with open(output_file, "w", encoding="utf-8") as f:
f.write("<!DOCTYPE html>\n<html>\n<head>\n")
f.write("<meta charset=\"utf-8\">\n")
f.write("<title>SpiceCode Analysis Results</title>\n")
f.write("<style>\n")
f.write("body { font-family: Arial, sans-serif; margin: 20px; }\n")
f.write("table { border-collapse: collapse; width: 100%; }\n")
f.write("th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }\n")
f.write("th { background-color: #f2f2f2; }\n")
f.write("h1 { color: #333; }\n")
f.write("</style>\n</head>\n<body>\n")
f.write(f"<h1>{messages.get('analysis_results', 'Analysis Results')}</h1>\n")
f.write(f"<p><strong>{messages.get('file_name', 'File')}: {results.get('file_name', 'Unknown')}</strong></p>\n")
f.write("<table>\n<tr><th>Metric</th><th>Value</th></tr>\n")
for key, value in results.items():
if isinstance(value, (int, float, str)):
f.write(f"<tr><td>{key.replace('_', ' ').title()}</td><td>{value}</td></tr>\n")
elif isinstance(value, list) and key == "indentation_levels":
f.write(f"<tr><td>{key.replace('_', ' ').title()}</td><td>{len(value)} levels</td></tr>\n")
f.write("</table>\n</body>\n</html>")

else:
return False

return True

except Exception as e:
print(f"{messages.get('export_error', 'Export error')}: {str(e)}")
return False

def export_command(file, format_type, output, LANG_FILE):
"""
Export analysis results to a file.
"""
# Load translations
messages = get_translation(LANG_FILE)
console = Console()

# Validate format type
valid_formats = ["json", "csv", "markdown", "html"]
if format_type not in valid_formats:
console.print(f"[red]{messages.get('invalid_format', 'Invalid format')}[/] {format_type}")
console.print(f"{messages.get('valid_formats', 'Valid formats')}: {', '.join(valid_formats)}")
return

try:
# Analyze file
from spice.analyze import analyze_file
results = analyze_file(file)

# Set default output file if not provided
if not output:
base_name = os.path.splitext(os.path.basename(file))[0]
output = f"{base_name}_analysis.{format_type}"

# Export results
success = export_results(results, format_type, output, messages)

if success:
console.print(f"[green]{messages.get('export_success', 'Export successful')}[/]: {output}")
else:
console.print(f"[red]{messages.get('export_failed', 'Export failed')}[/]")

except Exception as e:
console.print(f"[red]{messages.get('error', 'Error')}[/]: {str(e)}")
41 changes: 12 additions & 29 deletions cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from cli.commands.hello import hello_command
from cli.commands.version import version_command
from cli.commands.analyze import analyze_command

from cli.commands.export.export import export_command

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

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



# SPICE TRANSLATE COMMAND
@app.command()
def translate():
"""
Set the language for CLI messages.
"""

translate_command(LANG_FILE)

# -- end -- #


# SPICE HELLO COMMAND
@app.command()
def hello():
"""
Welcome message.
"""

hello_command(LANG_FILE)

# -- end -- #


# SPICE VERSION COMMAND
@app.command()
def version():
"""
Display the current version of the application.
"""

version_command(LANG_FILE, CURRENT_DIR)

#--- end ---#


# SPICE ANALYZE COMMAND
@app.command()
def analyze(
file: str,
Expand All @@ -69,20 +51,21 @@ def analyze(
"""
Analyze the given file.
"""

analyze_command(file, all, json_output, LANG_FILE)

# -- end -- #

@app.command()
def export(
file: str,
format_type: str = typer.Option("json", "--format", "-f", help="Export format (json, csv, markdown, html)"),
output: str = typer.Option(None, "--output", "-o", help="Output file path")
):
"""
Analyze a file and export results to a file in the specified format.
"""
export_command(file, format_type, output, LANG_FILE)

def main():
app() # run typer

# -- end -- #


# whatever the fuck this is python makes no sense
if __name__ == "__main__":
main()

# -- end -- #
19 changes: 16 additions & 3 deletions lexers/golang/golexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,21 @@ def tokenize_string(self):
elif char == "\\": # escape de caracteres
self.position += 1
self.column += 1
if self.position < len(self.source_code): # avança o caractere escapado
self.position += 1
self.column += 1
continue
elif char == "\n": # se tiver uma nova linha dentro da string
# Em Go, strings com aspas duplas não podem conter quebras de linha
# mas strings com crases (raw strings) podem
if quote_char == '"':
return Token(TokenType.ERROR, "string não fechada", self.line, start_col)
self.line += 1
self.column = 1
self.current_line_start = self.position + 1
else:
self.column += 1
self.position += 1
self.column += 1

string_value = self.source_code[start_pos:self.position] # pega o texto da string
return Token(TokenType.STRING, string_value, self.line, start_col)
Expand All @@ -210,9 +223,9 @@ def tokenize_identifier(self):
def match_operator(self):
"""tenta casar com um operador."""
for op in sorted(self.OPERATORS, key=len, reverse=True): # verifica operadores mais longos primeiro
if self.source_code.startswith(op, self.position):
if self.position + len(op) <= len(self.source_code) and self.source_code[self.position:self.position + len(op)] == op:
token = Token(TokenType.OPERATOR, op, self.line, self.column)
self.position += len(op)
self.column += len(op)
return token
return None
return None
Loading