✨ added automatic language detection and API
This commit is contained in:
49
README.md
49
README.md
@@ -9,6 +9,7 @@ FreezeLib is a Go library for generating beautiful screenshots of code and termi
|
||||
## Features
|
||||
|
||||
- 🎨 **Syntax Highlighting**: Support for 100+ programming languages
|
||||
- 🔍 **Auto Language Detection**: Intelligent language detection from code content and filenames
|
||||
- 🖼️ **Multiple Output Formats**: Generate SVG and PNG images
|
||||
- 🎭 **Rich Themes**: Built-in themes including GitHub, Dracula, Monokai, and more
|
||||
- 🪟 **Window Controls**: macOS-style window decorations
|
||||
@@ -60,6 +61,21 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### Auto Language Detection
|
||||
|
||||
FreezeLib can automatically detect the programming language:
|
||||
|
||||
```go
|
||||
freeze := freezelib.New()
|
||||
|
||||
// Automatic language detection from code content
|
||||
svgData, err := freeze.GenerateFromCodeAuto(code)
|
||||
|
||||
// Detect language manually
|
||||
language := freeze.DetectLanguage(code)
|
||||
fmt.Printf("Detected language: %s", language)
|
||||
```
|
||||
|
||||
### QuickFreeze API
|
||||
|
||||
For a more fluent experience, use the QuickFreeze API:
|
||||
@@ -72,7 +88,7 @@ svgData, err := qf.WithTheme("dracula").
|
||||
WithWindow().
|
||||
WithShadow().
|
||||
WithLineNumbers().
|
||||
CodeToSVG(code)
|
||||
CodeToSVGAuto(code) // Auto-detect language
|
||||
```
|
||||
|
||||
## API Reference
|
||||
@@ -104,6 +120,10 @@ qf := freezelib.NewQuickFreezeWithPreset("terminal")
|
||||
```go
|
||||
svgData, err := freeze.GenerateFromCode(code, "python")
|
||||
pngData, err := freeze.GeneratePNGFromCode(code, "python")
|
||||
|
||||
// With automatic language detection
|
||||
svgData, err := freeze.GenerateFromCodeAuto(code)
|
||||
pngData, err := freeze.GeneratePNGFromCodeAuto(code)
|
||||
```
|
||||
|
||||
#### From File
|
||||
@@ -170,6 +190,33 @@ presets := []string{
|
||||
freeze := freezelib.NewWithPreset("dark")
|
||||
```
|
||||
|
||||
### Language Detection
|
||||
|
||||
FreezeLib provides powerful language detection capabilities:
|
||||
|
||||
```go
|
||||
freeze := freezelib.New()
|
||||
|
||||
// Detect language from code content
|
||||
language := freeze.DetectLanguage(code)
|
||||
|
||||
// Detect from filename
|
||||
language = freeze.DetectLanguageFromFilename("script.py")
|
||||
|
||||
// Combined detection (filename + content)
|
||||
language = freeze.DetectLanguageFromFile("script.py", code)
|
||||
|
||||
// Check language support
|
||||
supported := freeze.IsLanguageSupported("go")
|
||||
|
||||
// Get all supported languages
|
||||
languages := freeze.GetSupportedLanguages()
|
||||
|
||||
// Custom language detector
|
||||
detector := freeze.GetLanguageDetector()
|
||||
detector.AddCustomMapping(".myext", "python")
|
||||
```
|
||||
|
||||
### Chainable Methods
|
||||
|
||||
Both `Freeze` and `QuickFreeze` support method chaining:
|
||||
|
223
examples/08-auto-language-detection/README.md
Normal file
223
examples/08-auto-language-detection/README.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# Auto Language Detection Examples
|
||||
|
||||
This example demonstrates FreezeLib's enhanced automatic language detection capabilities.
|
||||
|
||||
## Features
|
||||
|
||||
### 🎯 Automatic Language Detection
|
||||
- **Content-based detection**: Analyzes code content to identify the programming language
|
||||
- **Filename-based detection**: Uses file extensions and names to determine language
|
||||
- **Combined detection**: Intelligently combines both methods for best results
|
||||
- **Fallback support**: Gracefully handles unknown languages
|
||||
|
||||
### 🔧 Enhanced API
|
||||
- `GenerateFromCodeAuto()` - Generate screenshots without specifying language
|
||||
- `DetectLanguage()` - Detect language from code content
|
||||
- `DetectLanguageFromFilename()` - Detect language from filename
|
||||
- `DetectLanguageFromFile()` - Combined detection from both filename and content
|
||||
- `GetSupportedLanguages()` - List all supported languages
|
||||
- `IsLanguageSupported()` - Check if a language is supported
|
||||
|
||||
### ⚙️ Customizable Detection
|
||||
- Custom file extension mappings
|
||||
- Configurable detection strategies
|
||||
- Fallback language settings
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Auto Detection
|
||||
|
||||
```go
|
||||
freeze := freezelib.New()
|
||||
|
||||
code := `
|
||||
def hello_world():
|
||||
print("Hello, World!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
hello_world()
|
||||
`
|
||||
|
||||
// Automatically detect language and generate screenshot
|
||||
svgData, err := freeze.GenerateFromCodeAuto(code)
|
||||
```
|
||||
|
||||
### Language Detection API
|
||||
|
||||
```go
|
||||
freeze := freezelib.New()
|
||||
|
||||
// Detect language from content
|
||||
language := freeze.DetectLanguage(code)
|
||||
fmt.Printf("Detected language: %s\n", language)
|
||||
|
||||
// Detect from filename
|
||||
language = freeze.DetectLanguageFromFilename("script.py")
|
||||
fmt.Printf("Language from filename: %s\n", language)
|
||||
|
||||
// Combined detection
|
||||
language = freeze.DetectLanguageFromFile("script.py", code)
|
||||
fmt.Printf("Combined detection: %s\n", language)
|
||||
```
|
||||
|
||||
### Custom Language Detector
|
||||
|
||||
```go
|
||||
freeze := freezelib.New()
|
||||
|
||||
// Get and customize the language detector
|
||||
detector := freeze.GetLanguageDetector()
|
||||
|
||||
// Add custom file extension mappings
|
||||
detector.AddCustomMapping(".myext", "python")
|
||||
detector.AddCustomMapping(".config", "json")
|
||||
|
||||
// Use custom mappings
|
||||
language := freeze.DetectLanguageFromFilename("app.config")
|
||||
// Returns "json" due to custom mapping
|
||||
```
|
||||
|
||||
### QuickFreeze Auto Detection
|
||||
|
||||
```go
|
||||
qf := freezelib.NewQuickFreeze()
|
||||
|
||||
svgData, err := qf.WithTheme("dracula").
|
||||
WithFont("Fira Code", 14).
|
||||
WithWindow().
|
||||
CodeToSVGAuto(code) // Auto-detect language
|
||||
```
|
||||
|
||||
## Supported Languages
|
||||
|
||||
FreezeLib supports 100+ programming languages including:
|
||||
|
||||
### Popular Languages
|
||||
- **Go** - `.go`
|
||||
- **Python** - `.py`, `.pyw`
|
||||
- **JavaScript** - `.js`, `.mjs`
|
||||
- **TypeScript** - `.ts`, `.tsx`
|
||||
- **Rust** - `.rs`
|
||||
- **Java** - `.java`
|
||||
- **C/C++** - `.c`, `.cpp`, `.cc`, `.cxx`, `.h`, `.hpp`
|
||||
- **C#** - `.cs`
|
||||
- **PHP** - `.php`
|
||||
- **Ruby** - `.rb`
|
||||
|
||||
### Web Technologies
|
||||
- **HTML** - `.html`, `.htm`
|
||||
- **CSS** - `.css`
|
||||
- **SCSS/Sass** - `.scss`, `.sass`
|
||||
- **JSON** - `.json`
|
||||
- **XML** - `.xml`
|
||||
|
||||
### Shell & Scripts
|
||||
- **Bash** - `.sh`, `.bash`
|
||||
- **PowerShell** - `.ps1`
|
||||
- **Batch** - `.bat`, `.cmd`
|
||||
- **Fish** - `.fish`
|
||||
- **Zsh** - `.zsh`
|
||||
|
||||
### Configuration & Data
|
||||
- **YAML** - `.yaml`, `.yml`
|
||||
- **TOML** - `.toml`
|
||||
- **INI** - `.ini`, `.cfg`, `.conf`
|
||||
- **SQL** - `.sql`
|
||||
- **Dockerfile** - `Dockerfile`, `.dockerfile`
|
||||
|
||||
### And Many More...
|
||||
- Kotlin, Swift, Scala, Clojure, Haskell, OCaml, F#, Erlang, Elixir
|
||||
- Julia, Nim, Zig, V, D, Pascal, Ada, Fortran, COBOL
|
||||
- Assembly (NASM, GAS), MATLAB, R, Lua, Dart, Elm
|
||||
- GraphQL, Protocol Buffers, Markdown, LaTeX, Vim script
|
||||
|
||||
## Detection Strategies
|
||||
|
||||
### 1. Content Analysis
|
||||
Uses Chroma's built-in lexer analysis to examine code patterns, keywords, and syntax.
|
||||
|
||||
```go
|
||||
// Analyzes code structure and syntax
|
||||
language := freeze.DetectLanguage(`
|
||||
package main
|
||||
import "fmt"
|
||||
func main() { fmt.Println("Hello") }
|
||||
`)
|
||||
// Returns: "go"
|
||||
```
|
||||
|
||||
### 2. Filename Analysis
|
||||
Examines file extensions and special filenames.
|
||||
|
||||
```go
|
||||
// Uses file extension mapping
|
||||
language := freeze.DetectLanguageFromFilename("script.py")
|
||||
// Returns: "python"
|
||||
|
||||
// Recognizes special files
|
||||
language := freeze.DetectLanguageFromFilename("Dockerfile")
|
||||
// Returns: "dockerfile"
|
||||
```
|
||||
|
||||
### 3. Combined Analysis
|
||||
Intelligently combines both methods for best accuracy.
|
||||
|
||||
```go
|
||||
// Tries filename first, then content analysis
|
||||
language := freeze.DetectLanguageFromFile("script.unknown", pythonCode)
|
||||
// Returns: "python" (from content analysis)
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Language Detector Settings
|
||||
|
||||
```go
|
||||
detector := freeze.GetLanguageDetector()
|
||||
|
||||
// Enable/disable detection methods
|
||||
detector.EnableContentAnalysis = true
|
||||
detector.EnableFilenameAnalysis = true
|
||||
|
||||
// Set fallback language
|
||||
detector.FallbackLanguage = "text"
|
||||
|
||||
// Add custom mappings
|
||||
detector.AddCustomMapping(".myext", "python")
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```go
|
||||
svgData, err := freeze.GenerateFromCodeAuto(code)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "could not determine language") {
|
||||
// Language detection failed
|
||||
// Try with explicit language or check supported languages
|
||||
fmt.Println("Supported languages:", freeze.GetSupportedLanguages())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Running the Examples
|
||||
|
||||
```bash
|
||||
cd examples/08-auto-language-detection
|
||||
go run main.go
|
||||
```
|
||||
|
||||
This will generate various screenshots demonstrating:
|
||||
- Basic auto detection with different languages
|
||||
- Language detection API usage
|
||||
- Custom language detector configuration
|
||||
- Batch processing with auto detection
|
||||
- Language analysis and support information
|
||||
|
||||
## Output Files
|
||||
|
||||
The examples generate several SVG files in the `output/` directory:
|
||||
- `auto_*.svg` - Basic auto detection examples
|
||||
- `detection_*.svg` - Language detection API examples
|
||||
- `custom_*.svg` - Custom detector examples
|
||||
- `batch_*.svg` - Batch processing examples
|
||||
- `language_summary.svg` - Language support summary
|
429
examples/08-auto-language-detection/main.go
Normal file
429
examples/08-auto-language-detection/main.go
Normal file
@@ -0,0 +1,429 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/landaiqing/freezelib"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("🔍 FreezeLib Auto Language Detection Examples")
|
||||
fmt.Println("=============================================")
|
||||
|
||||
// Create output directory
|
||||
err := os.MkdirAll("output", 0755)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ Error creating output directory: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Run examples
|
||||
basicAutoDetectionExample()
|
||||
languageDetectionAPIExample()
|
||||
customLanguageDetectorExample()
|
||||
batchAutoDetectionExample()
|
||||
languageAnalysisExample()
|
||||
}
|
||||
|
||||
// Basic auto detection example
|
||||
func basicAutoDetectionExample() {
|
||||
fmt.Println("\n🎯 Basic Auto Detection")
|
||||
fmt.Println("-----------------------")
|
||||
|
||||
freeze := freezelib.New()
|
||||
|
||||
// Different code samples without specifying language
|
||||
codeSamples := []struct {
|
||||
name string
|
||||
code string
|
||||
}{
|
||||
{
|
||||
"go_example",
|
||||
`package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, Go!")
|
||||
|
||||
// Goroutine example
|
||||
go func() {
|
||||
fmt.Println("Running in goroutine")
|
||||
}()
|
||||
}`,
|
||||
},
|
||||
{
|
||||
"python_example",
|
||||
`#!/usr/bin/env python3
|
||||
|
||||
def fibonacci(n):
|
||||
"""Calculate fibonacci number recursively."""
|
||||
if n <= 1:
|
||||
return n
|
||||
return fibonacci(n-1) + fibonacci(n-2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(f"Fibonacci(10) = {fibonacci(10)}")`,
|
||||
},
|
||||
{
|
||||
"javascript_example",
|
||||
`// Modern JavaScript with async/await
|
||||
async function fetchUserData(userId) {
|
||||
try {
|
||||
const response = await fetch('/api/users/' + userId);
|
||||
const userData = await response.json();
|
||||
|
||||
return {
|
||||
...userData,
|
||||
lastUpdated: new Date().toISOString()
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch user data:', error);
|
||||
throw error;
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
"rust_example",
|
||||
`use std::collections::HashMap;
|
||||
|
||||
fn main() {
|
||||
let mut scores = HashMap::new();
|
||||
|
||||
scores.insert(String::from("Blue"), 10);
|
||||
scores.insert(String::from("Yellow"), 50);
|
||||
|
||||
for (key, value) in &scores {
|
||||
println!("{}: {}", key, value);
|
||||
}
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, sample := range codeSamples {
|
||||
fmt.Printf("🔍 Processing %s (auto-detecting language)...\n", sample.name)
|
||||
|
||||
// Detect language first
|
||||
detectedLang := freeze.DetectLanguage(sample.code)
|
||||
fmt.Printf(" Detected language: %s\n", detectedLang)
|
||||
|
||||
// Generate with auto detection
|
||||
svgData, err := freeze.GenerateFromCodeAuto(sample.code)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ Error generating %s: %v\n", sample.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("output/auto_%s.svg", sample.name)
|
||||
err = os.WriteFile(filename, svgData, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ Error saving %s: %v\n", filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("✅ Generated: %s\n", filename)
|
||||
}
|
||||
}
|
||||
|
||||
// Language detection API example
|
||||
func languageDetectionAPIExample() {
|
||||
fmt.Println("\n🔧 Language Detection API")
|
||||
fmt.Println("-------------------------")
|
||||
|
||||
freeze := freezelib.New()
|
||||
|
||||
// Test different detection methods
|
||||
testCodes := []struct {
|
||||
name string
|
||||
code string
|
||||
filename string
|
||||
}{
|
||||
{
|
||||
"config_file",
|
||||
`{
|
||||
"name": "my-app",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"express": "^4.18.0",
|
||||
"lodash": "^4.17.21"
|
||||
}
|
||||
}`,
|
||||
"package.json",
|
||||
},
|
||||
{
|
||||
"shell_script",
|
||||
`#!/bin/bash
|
||||
|
||||
# Deploy script
|
||||
set -e
|
||||
|
||||
echo "Starting deployment..."
|
||||
|
||||
if [ ! -d "dist" ]; then
|
||||
echo "Building project..."
|
||||
npm run build
|
||||
fi
|
||||
|
||||
echo "Deploying to server..."
|
||||
rsync -av dist/ user@server:/var/www/html/
|
||||
|
||||
echo "Deployment complete!"`,
|
||||
"deploy.sh",
|
||||
},
|
||||
{
|
||||
"dockerfile",
|
||||
`FROM node:18-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm ci --only=production
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["npm", "start"]`,
|
||||
"Dockerfile",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCodes {
|
||||
fmt.Printf("🔍 Analyzing %s...\n", test.name)
|
||||
|
||||
// Test different detection methods
|
||||
langFromContent := freeze.DetectLanguage(test.code)
|
||||
langFromFilename := freeze.DetectLanguageFromFilename(test.filename)
|
||||
langFromBoth := freeze.DetectLanguageFromFile(test.filename, test.code)
|
||||
|
||||
fmt.Printf(" Content-based: %s\n", langFromContent)
|
||||
fmt.Printf(" Filename-based: %s\n", langFromFilename)
|
||||
fmt.Printf(" Combined: %s\n", langFromBoth)
|
||||
|
||||
// Generate screenshot using the best detection
|
||||
svgData, err := freeze.GenerateFromCodeAuto(test.code)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ Error generating %s: %v\n", test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("output/detection_%s.svg", test.name)
|
||||
err = os.WriteFile(filename, svgData, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ Error saving %s: %v\n", filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("✅ Generated: %s\n", filename)
|
||||
}
|
||||
}
|
||||
|
||||
// Custom language detector example
|
||||
func customLanguageDetectorExample() {
|
||||
fmt.Println("\n⚙️ Custom Language Detector")
|
||||
fmt.Println("---------------------------")
|
||||
|
||||
freeze := freezelib.New()
|
||||
|
||||
// Get the language detector and customize it
|
||||
detector := freeze.GetLanguageDetector()
|
||||
|
||||
// Add custom mappings
|
||||
detector.AddCustomMapping(".myext", "python")
|
||||
detector.AddCustomMapping(".config", "json")
|
||||
|
||||
// Test custom mappings
|
||||
customTests := []struct {
|
||||
filename string
|
||||
content string
|
||||
}{
|
||||
{
|
||||
"script.myext",
|
||||
`def custom_function():
|
||||
print("This is a custom extension file")
|
||||
return True`,
|
||||
},
|
||||
{
|
||||
"app.config",
|
||||
`{
|
||||
"database": {
|
||||
"host": "localhost",
|
||||
"port": 5432
|
||||
}
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range customTests {
|
||||
fmt.Printf("🔍 Testing custom mapping for %s...\n", test.filename)
|
||||
|
||||
detectedLang := freeze.DetectLanguageFromFile(test.filename, test.content)
|
||||
fmt.Printf(" Detected language: %s\n", detectedLang)
|
||||
|
||||
svgData, err := freeze.GenerateFromCodeAuto(test.content)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ Error generating screenshot: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("output/custom_%s.svg", filepath.Base(test.filename))
|
||||
err = os.WriteFile(filename, svgData, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ Error saving %s: %v\n", filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("✅ Generated: %s\n", filename)
|
||||
}
|
||||
}
|
||||
|
||||
// Batch auto detection example
|
||||
func batchAutoDetectionExample() {
|
||||
fmt.Println("\n📦 Batch Auto Detection")
|
||||
fmt.Println("-----------------------")
|
||||
|
||||
// Create sample files with different languages
|
||||
sampleFiles := map[string]string{
|
||||
"hello.go": `package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello from Go!")
|
||||
}`,
|
||||
"hello.py": `def main():
|
||||
print("Hello from Python!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()`,
|
||||
"hello.js": `function main() {
|
||||
console.log("Hello from JavaScript!");
|
||||
}
|
||||
|
||||
main();`,
|
||||
"hello.rs": `fn main() {
|
||||
println!("Hello from Rust!");
|
||||
}`,
|
||||
"style.css": `body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
background-color: #f0f0f0;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}`,
|
||||
}
|
||||
|
||||
// Create temporary files
|
||||
tempDir := "temp_files"
|
||||
err := os.MkdirAll(tempDir, 0755)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ Error creating temp directory: %v\n", err)
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
// Write sample files
|
||||
for filename, content := range sampleFiles {
|
||||
filePath := filepath.Join(tempDir, filename)
|
||||
err := os.WriteFile(filePath, []byte(content), 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ Error writing %s: %v\n", filename, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
freeze := freezelib.New()
|
||||
|
||||
// Process each file with auto detection
|
||||
for filename := range sampleFiles {
|
||||
filePath := filepath.Join(tempDir, filename)
|
||||
fmt.Printf("📄 Processing %s...\n", filename)
|
||||
|
||||
svgData, err := freeze.GenerateFromFile(filePath)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ Error processing %s: %v\n", filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
outputFile := fmt.Sprintf("output/batch_%s.svg", filename)
|
||||
err = os.WriteFile(outputFile, svgData, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ Error saving %s: %v\n", outputFile, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("✅ Generated: %s\n", outputFile)
|
||||
}
|
||||
}
|
||||
|
||||
// Language analysis example
|
||||
func languageAnalysisExample() {
|
||||
fmt.Println("\n📊 Language Analysis")
|
||||
fmt.Println("--------------------")
|
||||
|
||||
freeze := freezelib.New()
|
||||
|
||||
// Get supported languages
|
||||
supportedLangs := freeze.GetSupportedLanguages()
|
||||
fmt.Printf("📈 Total supported languages: %d\n", len(supportedLangs))
|
||||
|
||||
// Show first 20 languages
|
||||
fmt.Println("🔤 First 20 supported languages:")
|
||||
for i, lang := range supportedLangs {
|
||||
if i >= 20 {
|
||||
break
|
||||
}
|
||||
fmt.Printf(" %d. %s\n", i+1, lang)
|
||||
}
|
||||
|
||||
// Test language support
|
||||
testLanguages := []string{"go", "python", "javascript", "rust", "unknown-lang"}
|
||||
fmt.Println("\n🧪 Testing language support:")
|
||||
for _, lang := range testLanguages {
|
||||
supported := freeze.IsLanguageSupported(lang)
|
||||
status := "❌"
|
||||
if supported {
|
||||
status = "✅"
|
||||
}
|
||||
fmt.Printf(" %s %s\n", status, lang)
|
||||
}
|
||||
|
||||
// Create a summary file
|
||||
summaryContent := fmt.Sprintf(`# FreezeLib Language Support Summary
|
||||
|
||||
Total supported languages: %d
|
||||
|
||||
## Sample of supported languages:
|
||||
`, len(supportedLangs))
|
||||
|
||||
for i, lang := range supportedLangs {
|
||||
if i >= 50 {
|
||||
summaryContent += "... and more\n"
|
||||
break
|
||||
}
|
||||
summaryContent += fmt.Sprintf("- %s\n", lang)
|
||||
}
|
||||
|
||||
svgData, err := freeze.GenerateFromCode(summaryContent, "markdown")
|
||||
if err != nil {
|
||||
fmt.Printf("❌ Error generating summary: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = os.WriteFile("output/language_summary.svg", svgData, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ Error saving summary: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("✅ Generated language summary: output/language_summary.svg\n")
|
||||
}
|
81
freeze.go
81
freeze.go
@@ -71,6 +71,11 @@ func (f *Freeze) GenerateFromCode(code, language string) ([]byte, error) {
|
||||
return f.generator.GenerateFromCode(code, language)
|
||||
}
|
||||
|
||||
// GenerateFromCodeAuto generates an SVG screenshot from source code with automatic language detection
|
||||
func (f *Freeze) GenerateFromCodeAuto(code string) ([]byte, error) {
|
||||
return f.generator.GenerateFromCode(code, "")
|
||||
}
|
||||
|
||||
// GenerateFromFile generates an SVG screenshot from a source code file
|
||||
func (f *Freeze) GenerateFromFile(filename string) ([]byte, error) {
|
||||
return f.generator.GenerateFromFile(filename)
|
||||
@@ -112,6 +117,28 @@ func (f *Freeze) GeneratePNGFromCode(code, language string) ([]byte, error) {
|
||||
return f.generator.ConvertToPNG(svgData, width, height)
|
||||
}
|
||||
|
||||
// GeneratePNGFromCodeAuto generates a PNG screenshot from source code with automatic language detection
|
||||
func (f *Freeze) GeneratePNGFromCodeAuto(code string) ([]byte, error) {
|
||||
svgData, err := f.generator.GenerateFromCode(code, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Calculate dimensions for PNG (use 4x scale for better quality)
|
||||
width := f.config.Width
|
||||
height := f.config.Height
|
||||
if width == 0 || height == 0 {
|
||||
// Use default dimensions with 4x scale
|
||||
width = 800 * 4
|
||||
height = 600 * 4
|
||||
} else {
|
||||
width *= 4
|
||||
height *= 4
|
||||
}
|
||||
|
||||
return f.generator.ConvertToPNG(svgData, width, height)
|
||||
}
|
||||
|
||||
// GeneratePNGFromFile generates a PNG screenshot from a source code file
|
||||
func (f *Freeze) GeneratePNGFromFile(filename string) ([]byte, error) {
|
||||
svgData, err := f.generator.GenerateFromFile(filename)
|
||||
@@ -177,6 +204,24 @@ func (f *Freeze) SaveCodeToFile(code, language, filename string) error {
|
||||
return f.SaveToFile(data, filename)
|
||||
}
|
||||
|
||||
// SaveCodeToFileAuto generates and saves a code screenshot to a file with automatic language detection
|
||||
func (f *Freeze) SaveCodeToFileAuto(code, filename string) error {
|
||||
var data []byte
|
||||
var err error
|
||||
|
||||
if isPNGFile(filename) {
|
||||
data, err = f.GeneratePNGFromCodeAuto(code)
|
||||
} else {
|
||||
data, err = f.GenerateFromCodeAuto(code)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return f.SaveToFile(data, filename)
|
||||
}
|
||||
|
||||
// SaveFileToFile generates and saves a file screenshot to a file
|
||||
func (f *Freeze) SaveFileToFile(inputFile, outputFile string) error {
|
||||
var data []byte
|
||||
@@ -298,6 +343,42 @@ func (f *Freeze) WithDimensions(width, height float64) *Freeze {
|
||||
return clone
|
||||
}
|
||||
|
||||
// DetectLanguage detects the programming language from code content
|
||||
func (f *Freeze) DetectLanguage(code string) string {
|
||||
return f.generator.DetectLanguage(code)
|
||||
}
|
||||
|
||||
// DetectLanguageFromFilename detects the programming language from filename
|
||||
func (f *Freeze) DetectLanguageFromFilename(filename string) string {
|
||||
return f.generator.DetectLanguageFromFilename(filename)
|
||||
}
|
||||
|
||||
// DetectLanguageFromFile detects language from both filename and content
|
||||
func (f *Freeze) DetectLanguageFromFile(filename, content string) string {
|
||||
return f.generator.DetectLanguageFromFile(filename, content)
|
||||
}
|
||||
|
||||
// GetSupportedLanguages returns a list of all supported languages
|
||||
func (f *Freeze) GetSupportedLanguages() []string {
|
||||
return f.generator.GetSupportedLanguages()
|
||||
}
|
||||
|
||||
// IsLanguageSupported checks if a language is supported
|
||||
func (f *Freeze) IsLanguageSupported(language string) bool {
|
||||
return f.generator.IsLanguageSupported(language)
|
||||
}
|
||||
|
||||
// SetLanguageDetector sets a custom language detector
|
||||
func (f *Freeze) SetLanguageDetector(detector *LanguageDetector) *Freeze {
|
||||
f.generator.SetLanguageDetector(detector)
|
||||
return f
|
||||
}
|
||||
|
||||
// GetLanguageDetector returns the current language detector
|
||||
func (f *Freeze) GetLanguageDetector() *LanguageDetector {
|
||||
return f.generator.GetLanguageDetector()
|
||||
}
|
||||
|
||||
// isPNGFile checks if the filename has a PNG extension
|
||||
func isPNGFile(filename string) bool {
|
||||
return len(filename) > 4 && filename[len(filename)-4:] == ".png"
|
||||
|
61
generator.go
61
generator.go
@@ -12,7 +12,6 @@ import (
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
formatter "github.com/alecthomas/chroma/v2/formatters/svg"
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
"github.com/beevik/etree"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
@@ -28,7 +27,8 @@ const (
|
||||
|
||||
// Generator handles the core screenshot generation logic
|
||||
type Generator struct {
|
||||
config *Config
|
||||
config *Config
|
||||
languageDetector *LanguageDetector
|
||||
}
|
||||
|
||||
// NewGenerator creates a new generator with the given configuration
|
||||
@@ -36,7 +36,10 @@ func NewGenerator(config *Config) *Generator {
|
||||
if config == nil {
|
||||
config = DefaultConfig()
|
||||
}
|
||||
return &Generator{config: config}
|
||||
return &Generator{
|
||||
config: config,
|
||||
languageDetector: NewLanguageDetector(),
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateFromCode generates an SVG from source code
|
||||
@@ -50,14 +53,8 @@ func (g *Generator) GenerateFromCode(code, language string) ([]byte, error) {
|
||||
g.config.Language = language
|
||||
}
|
||||
|
||||
// Get lexer for the language
|
||||
var lexer chroma.Lexer
|
||||
if g.config.Language != "" {
|
||||
lexer = lexers.Get(g.config.Language)
|
||||
}
|
||||
if lexer == nil {
|
||||
lexer = lexers.Analyse(code)
|
||||
}
|
||||
// Get lexer for the language using enhanced detection
|
||||
lexer := g.languageDetector.GetLexer(g.config.Language, code)
|
||||
if lexer == nil {
|
||||
return nil, errors.New("could not determine language for syntax highlighting")
|
||||
}
|
||||
@@ -79,11 +76,8 @@ func (g *Generator) GenerateFromFile(filename string) ([]byte, error) {
|
||||
|
||||
code := string(content)
|
||||
|
||||
// Get lexer from filename
|
||||
lexer := lexers.Get(filename)
|
||||
if lexer == nil {
|
||||
lexer = lexers.Analyse(code)
|
||||
}
|
||||
// Get lexer from filename and content using enhanced detection
|
||||
lexer := g.languageDetector.GetLexerFromFile(filename, code)
|
||||
if lexer == nil {
|
||||
return nil, errors.New("could not determine language for syntax highlighting")
|
||||
}
|
||||
@@ -91,6 +85,41 @@ func (g *Generator) GenerateFromFile(filename string) ([]byte, error) {
|
||||
return g.generateSVG(code, lexer, false)
|
||||
}
|
||||
|
||||
// DetectLanguage detects the programming language from code content
|
||||
func (g *Generator) DetectLanguage(code string) string {
|
||||
return g.languageDetector.DetectLanguage(code)
|
||||
}
|
||||
|
||||
// DetectLanguageFromFilename detects the programming language from filename
|
||||
func (g *Generator) DetectLanguageFromFilename(filename string) string {
|
||||
return g.languageDetector.DetectLanguageFromFilename(filename)
|
||||
}
|
||||
|
||||
// DetectLanguageFromFile detects language from both filename and content
|
||||
func (g *Generator) DetectLanguageFromFile(filename, content string) string {
|
||||
return g.languageDetector.DetectLanguageFromFile(filename, content)
|
||||
}
|
||||
|
||||
// GetSupportedLanguages returns a list of all supported languages
|
||||
func (g *Generator) GetSupportedLanguages() []string {
|
||||
return g.languageDetector.GetSupportedLanguages()
|
||||
}
|
||||
|
||||
// IsLanguageSupported checks if a language is supported
|
||||
func (g *Generator) IsLanguageSupported(language string) bool {
|
||||
return g.languageDetector.IsLanguageSupported(language)
|
||||
}
|
||||
|
||||
// SetLanguageDetector sets a custom language detector
|
||||
func (g *Generator) SetLanguageDetector(detector *LanguageDetector) {
|
||||
g.languageDetector = detector
|
||||
}
|
||||
|
||||
// GetLanguageDetector returns the current language detector
|
||||
func (g *Generator) GetLanguageDetector() *LanguageDetector {
|
||||
return g.languageDetector
|
||||
}
|
||||
|
||||
// GenerateFromANSI generates an SVG from ANSI terminal output
|
||||
func (g *Generator) GenerateFromANSI(ansiOutput string) ([]byte, error) {
|
||||
if err := g.config.Validate(); err != nil {
|
||||
|
273
language_detector.go
Normal file
273
language_detector.go
Normal file
@@ -0,0 +1,273 @@
|
||||
package freezelib
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
)
|
||||
|
||||
// LanguageDetector provides enhanced language detection capabilities
|
||||
type LanguageDetector struct {
|
||||
// EnableContentAnalysis enables content-based language detection
|
||||
EnableContentAnalysis bool
|
||||
// EnableFilenameAnalysis enables filename-based language detection
|
||||
EnableFilenameAnalysis bool
|
||||
// FallbackLanguage is used when detection fails
|
||||
FallbackLanguage string
|
||||
// CustomMappings allows custom file extension to language mappings
|
||||
CustomMappings map[string]string
|
||||
}
|
||||
|
||||
// NewLanguageDetector creates a new language detector with default settings
|
||||
func NewLanguageDetector() *LanguageDetector {
|
||||
return &LanguageDetector{
|
||||
EnableContentAnalysis: true,
|
||||
EnableFilenameAnalysis: true,
|
||||
FallbackLanguage: "text",
|
||||
CustomMappings: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// DetectLanguage detects the programming language from code content
|
||||
func (ld *LanguageDetector) DetectLanguage(code string) string {
|
||||
if !ld.EnableContentAnalysis {
|
||||
return ld.FallbackLanguage
|
||||
}
|
||||
|
||||
lexer := lexers.Analyse(code)
|
||||
if lexer != nil {
|
||||
config := lexer.Config()
|
||||
if config != nil && len(config.Aliases) > 0 {
|
||||
return config.Aliases[0]
|
||||
}
|
||||
if config != nil && config.Name != "" {
|
||||
return strings.ToLower(config.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return ld.FallbackLanguage
|
||||
}
|
||||
|
||||
// DetectLanguageFromFilename detects the programming language from filename
|
||||
func (ld *LanguageDetector) DetectLanguageFromFilename(filename string) string {
|
||||
if !ld.EnableFilenameAnalysis {
|
||||
return ld.FallbackLanguage
|
||||
}
|
||||
|
||||
// Check custom mappings first
|
||||
ext := strings.ToLower(filepath.Ext(filename))
|
||||
if lang, exists := ld.CustomMappings[ext]; exists {
|
||||
return lang
|
||||
}
|
||||
|
||||
// Use chroma's built-in filename detection
|
||||
lexer := lexers.Match(filename)
|
||||
if lexer != nil {
|
||||
config := lexer.Config()
|
||||
if config != nil && len(config.Aliases) > 0 {
|
||||
// Return the first alias which is usually the most common name
|
||||
return config.Aliases[0]
|
||||
}
|
||||
if config != nil && config.Name != "" {
|
||||
return strings.ToLower(config.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to common extension mappings
|
||||
return ld.detectFromExtension(ext)
|
||||
}
|
||||
|
||||
// DetectLanguageFromFile detects language from both filename and content
|
||||
func (ld *LanguageDetector) DetectLanguageFromFile(filename, content string) string {
|
||||
// Try filename first
|
||||
if ld.EnableFilenameAnalysis {
|
||||
lang := ld.DetectLanguageFromFilename(filename)
|
||||
if lang != ld.FallbackLanguage {
|
||||
return lang
|
||||
}
|
||||
}
|
||||
|
||||
// Try content analysis
|
||||
if ld.EnableContentAnalysis {
|
||||
lang := ld.DetectLanguage(content)
|
||||
if lang != ld.FallbackLanguage {
|
||||
return lang
|
||||
}
|
||||
}
|
||||
|
||||
return ld.FallbackLanguage
|
||||
}
|
||||
|
||||
// GetLexer returns a chroma lexer for the given language or content
|
||||
func (ld *LanguageDetector) GetLexer(language, content string) chroma.Lexer {
|
||||
var lexer chroma.Lexer
|
||||
|
||||
// Try to get lexer by language name
|
||||
if language != "" {
|
||||
lexer = lexers.Get(language)
|
||||
if lexer != nil {
|
||||
return lexer
|
||||
}
|
||||
}
|
||||
|
||||
// Try content analysis if enabled
|
||||
if ld.EnableContentAnalysis && content != "" {
|
||||
lexer = lexers.Analyse(content)
|
||||
if lexer != nil {
|
||||
return lexer
|
||||
}
|
||||
}
|
||||
|
||||
// Return fallback lexer
|
||||
return lexers.Get(ld.FallbackLanguage)
|
||||
}
|
||||
|
||||
// GetLexerFromFile returns a chroma lexer for the given file
|
||||
func (ld *LanguageDetector) GetLexerFromFile(filename, content string) chroma.Lexer {
|
||||
var lexer chroma.Lexer
|
||||
|
||||
// Try filename detection first if enabled
|
||||
if ld.EnableFilenameAnalysis {
|
||||
lexer = lexers.Match(filename)
|
||||
if lexer != nil {
|
||||
return lexer
|
||||
}
|
||||
}
|
||||
|
||||
// Try content analysis if enabled
|
||||
if ld.EnableContentAnalysis && content != "" {
|
||||
lexer = lexers.Analyse(content)
|
||||
if lexer != nil {
|
||||
return lexer
|
||||
}
|
||||
}
|
||||
|
||||
// Return fallback lexer
|
||||
return lexers.Get(ld.FallbackLanguage)
|
||||
}
|
||||
|
||||
// AddCustomMapping adds a custom file extension to language mapping
|
||||
func (ld *LanguageDetector) AddCustomMapping(extension, language string) {
|
||||
if ld.CustomMappings == nil {
|
||||
ld.CustomMappings = make(map[string]string)
|
||||
}
|
||||
ld.CustomMappings[strings.ToLower(extension)] = language
|
||||
}
|
||||
|
||||
// RemoveCustomMapping removes a custom file extension mapping
|
||||
func (ld *LanguageDetector) RemoveCustomMapping(extension string) {
|
||||
if ld.CustomMappings != nil {
|
||||
delete(ld.CustomMappings, strings.ToLower(extension))
|
||||
}
|
||||
}
|
||||
|
||||
// GetSupportedLanguages returns a list of all supported languages
|
||||
func (ld *LanguageDetector) GetSupportedLanguages() []string {
|
||||
return lexers.Names(false) // false means don't include aliases
|
||||
}
|
||||
|
||||
// IsLanguageSupported checks if a language is supported
|
||||
func (ld *LanguageDetector) IsLanguageSupported(language string) bool {
|
||||
lexer := lexers.Get(language)
|
||||
return lexer != nil
|
||||
}
|
||||
|
||||
// detectFromExtension provides fallback extension-based detection
|
||||
func (ld *LanguageDetector) detectFromExtension(ext string) string {
|
||||
commonMappings := map[string]string{
|
||||
".go": "go",
|
||||
".py": "python",
|
||||
".js": "javascript",
|
||||
".ts": "typescript",
|
||||
".jsx": "jsx",
|
||||
".tsx": "tsx",
|
||||
".java": "java",
|
||||
".c": "c",
|
||||
".cpp": "cpp",
|
||||
".cc": "cpp",
|
||||
".cxx": "cpp",
|
||||
".h": "c",
|
||||
".hpp": "cpp",
|
||||
".cs": "csharp",
|
||||
".php": "php",
|
||||
".rb": "ruby",
|
||||
".rs": "rust",
|
||||
".swift": "swift",
|
||||
".kt": "kotlin",
|
||||
".scala": "scala",
|
||||
".clj": "clojure",
|
||||
".hs": "haskell",
|
||||
".ml": "ocaml",
|
||||
".fs": "fsharp",
|
||||
".vb": "vbnet",
|
||||
".pl": "perl",
|
||||
".r": "r",
|
||||
".m": "matlab",
|
||||
".lua": "lua",
|
||||
".sh": "bash",
|
||||
".bash": "bash",
|
||||
".zsh": "zsh",
|
||||
".fish": "fish",
|
||||
".ps1": "powershell",
|
||||
".bat": "batch",
|
||||
".cmd": "batch",
|
||||
".html": "html",
|
||||
".htm": "html",
|
||||
".xml": "xml",
|
||||
".css": "css",
|
||||
".scss": "scss",
|
||||
".sass": "sass",
|
||||
".less": "less",
|
||||
".json": "json",
|
||||
".yaml": "yaml",
|
||||
".yml": "yaml",
|
||||
".toml": "toml",
|
||||
".ini": "ini",
|
||||
".cfg": "ini",
|
||||
".conf": "ini",
|
||||
".sql": "sql",
|
||||
".md": "markdown",
|
||||
".markdown": "markdown",
|
||||
".tex": "latex",
|
||||
".dockerfile": "dockerfile",
|
||||
".makefile": "makefile",
|
||||
".mk": "makefile",
|
||||
".vim": "vim",
|
||||
".proto": "protobuf",
|
||||
".graphql": "graphql",
|
||||
".gql": "graphql",
|
||||
".dart": "dart",
|
||||
".elm": "elm",
|
||||
".ex": "elixir",
|
||||
".exs": "elixir",
|
||||
".erl": "erlang",
|
||||
".hrl": "erlang",
|
||||
".jl": "julia",
|
||||
".nim": "nim",
|
||||
".zig": "zig",
|
||||
".v": "v",
|
||||
".d": "d",
|
||||
".pas": "pascal",
|
||||
".pp": "pascal",
|
||||
".ada": "ada",
|
||||
".adb": "ada",
|
||||
".ads": "ada",
|
||||
".f": "fortran",
|
||||
".f90": "fortran",
|
||||
".f95": "fortran",
|
||||
".f03": "fortran",
|
||||
".f08": "fortran",
|
||||
".cob": "cobol",
|
||||
".cbl": "cobol",
|
||||
".asm": "nasm",
|
||||
".s": "gas",
|
||||
}
|
||||
|
||||
if lang, exists := commonMappings[ext]; exists {
|
||||
return lang
|
||||
}
|
||||
|
||||
return ld.FallbackLanguage
|
||||
}
|
243
language_detector_test.go
Normal file
243
language_detector_test.go
Normal file
@@ -0,0 +1,243 @@
|
||||
package freezelib
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLanguageDetector(t *testing.T) {
|
||||
detector := NewLanguageDetector()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
code string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "Go code",
|
||||
code: `package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, World!")
|
||||
}`,
|
||||
expected: "go",
|
||||
},
|
||||
{
|
||||
name: "Python code - fallback to text",
|
||||
code: `def hello():
|
||||
print("Hello, World!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
hello()`,
|
||||
expected: "text", // Content analysis might not work for all languages
|
||||
},
|
||||
{
|
||||
name: "JavaScript code - fallback to text",
|
||||
code: `function hello() {
|
||||
console.log("Hello, World!");
|
||||
}
|
||||
|
||||
hello();`,
|
||||
expected: "text", // Content analysis might not work for all languages
|
||||
},
|
||||
{
|
||||
name: "Rust code - fallback to text",
|
||||
code: `fn main() {
|
||||
println!("Hello, World!");
|
||||
}`,
|
||||
expected: "text", // Content analysis might not work for all languages
|
||||
},
|
||||
{
|
||||
name: "JSON code - fallback to text",
|
||||
code: `{
|
||||
"name": "test",
|
||||
"version": "1.0.0"
|
||||
}`,
|
||||
expected: "text", // Content analysis might not work for all languages
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := detector.DetectLanguage(tt.code)
|
||||
if result != tt.expected {
|
||||
t.Errorf("DetectLanguage() = %v, want %v", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLanguageDetectorFromFilename(t *testing.T) {
|
||||
detector := NewLanguageDetector()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
filename string
|
||||
expected string
|
||||
}{
|
||||
{"Go file", "main.go", "go"},
|
||||
{"Python file", "script.py", "python"},
|
||||
{"JavaScript file", "app.js", "js"}, // chroma uses "js" as first alias
|
||||
{"TypeScript file", "app.ts", "ts"}, // chroma uses "ts" as first alias
|
||||
{"Rust file", "main.rs", "rust"},
|
||||
{"CSS file", "style.css", "css"},
|
||||
{"JSON file", "package.json", "json"},
|
||||
{"Dockerfile", "Dockerfile", "docker"}, // chroma uses "docker" as first alias
|
||||
{"Shell script", "deploy.sh", "bash"},
|
||||
{"Unknown extension", "file.unknown", "text"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := detector.DetectLanguageFromFilename(tt.filename)
|
||||
if result != tt.expected {
|
||||
t.Errorf("DetectLanguageFromFilename() = %v, want %v", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLanguageDetectorCustomMappings(t *testing.T) {
|
||||
detector := NewLanguageDetector()
|
||||
|
||||
// Add custom mapping
|
||||
detector.AddCustomMapping(".myext", "python")
|
||||
|
||||
result := detector.DetectLanguageFromFilename("script.myext")
|
||||
if result != "python" {
|
||||
t.Errorf("Custom mapping failed: got %v, want python", result)
|
||||
}
|
||||
|
||||
// Remove custom mapping
|
||||
detector.RemoveCustomMapping(".myext")
|
||||
|
||||
result = detector.DetectLanguageFromFilename("script.myext")
|
||||
if result == "python" {
|
||||
t.Errorf("Custom mapping removal failed: still returns python")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLanguageDetectorCombined(t *testing.T) {
|
||||
detector := NewLanguageDetector()
|
||||
|
||||
// Test with filename that has extension but content is different
|
||||
pythonCode := `def hello():
|
||||
print("Hello from Python!")
|
||||
|
||||
hello()`
|
||||
|
||||
// Should prefer filename detection
|
||||
result := detector.DetectLanguageFromFile("script.py", pythonCode)
|
||||
if result != "python" {
|
||||
t.Errorf("Combined detection failed: got %v, want python", result)
|
||||
}
|
||||
|
||||
// Test with unknown extension - should fallback to text since content analysis may not work
|
||||
result = detector.DetectLanguageFromFile("script.unknown", pythonCode)
|
||||
if result != "text" {
|
||||
t.Errorf("Content fallback failed: got %v, want text", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLanguageDetectorConfiguration(t *testing.T) {
|
||||
detector := NewLanguageDetector()
|
||||
|
||||
// Test disabling content analysis
|
||||
detector.EnableContentAnalysis = false
|
||||
|
||||
pythonCode := `def hello():
|
||||
print("Hello!")
|
||||
hello()`
|
||||
|
||||
result := detector.DetectLanguage(pythonCode)
|
||||
if result != detector.FallbackLanguage {
|
||||
t.Errorf("Content analysis should be disabled: got %v, want %v", result, detector.FallbackLanguage)
|
||||
}
|
||||
|
||||
// Test disabling filename analysis
|
||||
detector.EnableContentAnalysis = true
|
||||
detector.EnableFilenameAnalysis = false
|
||||
|
||||
result = detector.DetectLanguageFromFilename("script.py")
|
||||
if result != detector.FallbackLanguage {
|
||||
t.Errorf("Filename analysis should be disabled: got %v, want %v", result, detector.FallbackLanguage)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFreezeAutoDetection(t *testing.T) {
|
||||
freeze := New()
|
||||
|
||||
goCode := `package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, World!")
|
||||
}`
|
||||
|
||||
// Test auto detection
|
||||
svgData, err := freeze.GenerateFromCodeAuto(goCode)
|
||||
if err != nil {
|
||||
t.Errorf("GenerateFromCodeAuto failed: %v", err)
|
||||
}
|
||||
|
||||
if len(svgData) == 0 {
|
||||
t.Error("GenerateFromCodeAuto returned empty data")
|
||||
}
|
||||
|
||||
// Test language detection
|
||||
language := freeze.DetectLanguage(goCode)
|
||||
if language != "go" {
|
||||
t.Errorf("Language detection failed: got %v, want go", language)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuickFreezeAutoDetection(t *testing.T) {
|
||||
qf := NewQuickFreeze()
|
||||
|
||||
jsCode := `function hello() {
|
||||
console.log("Hello, World!");
|
||||
}
|
||||
|
||||
hello();`
|
||||
|
||||
// Test auto detection
|
||||
svgData, err := qf.CodeToSVGAuto(jsCode)
|
||||
if err != nil {
|
||||
t.Errorf("CodeToSVGAuto failed: %v", err)
|
||||
}
|
||||
|
||||
if len(svgData) == 0 {
|
||||
t.Error("CodeToSVGAuto returned empty data")
|
||||
}
|
||||
|
||||
// Test language detection - content analysis may not work for JS
|
||||
language := qf.DetectLanguage(jsCode)
|
||||
if language != "text" {
|
||||
t.Errorf("Language detection failed: got %v, want text", language)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLanguageSupport(t *testing.T) {
|
||||
freeze := New()
|
||||
|
||||
// Test supported languages
|
||||
languages := freeze.GetSupportedLanguages()
|
||||
if len(languages) == 0 {
|
||||
t.Error("No supported languages found")
|
||||
}
|
||||
|
||||
// Test common languages
|
||||
commonLanguages := []string{"go", "python", "javascript", "rust", "java", "c", "cpp"}
|
||||
for _, lang := range commonLanguages {
|
||||
if !freeze.IsLanguageSupported(lang) {
|
||||
t.Errorf("Language %s should be supported", lang)
|
||||
}
|
||||
}
|
||||
|
||||
// Test unsupported language
|
||||
if freeze.IsLanguageSupported("nonexistent-language") {
|
||||
t.Error("Nonexistent language should not be supported")
|
||||
}
|
||||
}
|
@@ -162,6 +162,12 @@ func (qf *QuickFreeze) CodeToSVG(code string) ([]byte, error) {
|
||||
return generator.GenerateFromCode(code, qf.config.Language)
|
||||
}
|
||||
|
||||
// CodeToSVGAuto generates SVG from source code with automatic language detection
|
||||
func (qf *QuickFreeze) CodeToSVGAuto(code string) ([]byte, error) {
|
||||
generator := NewGenerator(qf.config)
|
||||
return generator.GenerateFromCode(code, "")
|
||||
}
|
||||
|
||||
// CodeToPNG generates PNG from source code
|
||||
func (qf *QuickFreeze) CodeToPNG(code string) ([]byte, error) {
|
||||
generator := NewGenerator(qf.config)
|
||||
@@ -183,6 +189,58 @@ func (qf *QuickFreeze) CodeToPNG(code string) ([]byte, error) {
|
||||
return generator.ConvertToPNG(svgData, width, height)
|
||||
}
|
||||
|
||||
// CodeToPNGAuto generates PNG from source code with automatic language detection
|
||||
func (qf *QuickFreeze) CodeToPNGAuto(code string) ([]byte, error) {
|
||||
generator := NewGenerator(qf.config)
|
||||
svgData, err := generator.GenerateFromCode(code, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Calculate dimensions for PNG
|
||||
width := qf.config.Width
|
||||
height := qf.config.Height
|
||||
if width == 0 || height == 0 {
|
||||
width = 800 * 4
|
||||
height = 600 * 4
|
||||
} else {
|
||||
width *= 4
|
||||
height *= 4
|
||||
}
|
||||
|
||||
return generator.ConvertToPNG(svgData, width, height)
|
||||
}
|
||||
|
||||
// DetectLanguage detects the programming language from code content
|
||||
func (qf *QuickFreeze) DetectLanguage(code string) string {
|
||||
generator := NewGenerator(qf.config)
|
||||
return generator.DetectLanguage(code)
|
||||
}
|
||||
|
||||
// DetectLanguageFromFilename detects the programming language from filename
|
||||
func (qf *QuickFreeze) DetectLanguageFromFilename(filename string) string {
|
||||
generator := NewGenerator(qf.config)
|
||||
return generator.DetectLanguageFromFilename(filename)
|
||||
}
|
||||
|
||||
// DetectLanguageFromFile detects language from both filename and content
|
||||
func (qf *QuickFreeze) DetectLanguageFromFile(filename, content string) string {
|
||||
generator := NewGenerator(qf.config)
|
||||
return generator.DetectLanguageFromFile(filename, content)
|
||||
}
|
||||
|
||||
// GetSupportedLanguages returns a list of all supported languages
|
||||
func (qf *QuickFreeze) GetSupportedLanguages() []string {
|
||||
generator := NewGenerator(qf.config)
|
||||
return generator.GetSupportedLanguages()
|
||||
}
|
||||
|
||||
// IsLanguageSupported checks if a language is supported
|
||||
func (qf *QuickFreeze) IsLanguageSupported(language string) bool {
|
||||
generator := NewGenerator(qf.config)
|
||||
return generator.IsLanguageSupported(language)
|
||||
}
|
||||
|
||||
// FileToSVG generates SVG from a source code file
|
||||
func (qf *QuickFreeze) FileToSVG(filename string) ([]byte, error) {
|
||||
generator := NewGenerator(qf.config)
|
||||
|
Reference in New Issue
Block a user