AumCoreAI commited on
Commit
96a6806
·
verified ·
1 Parent(s): 3b3c0f9

Update modules/code_formatter.py

Browse files
Files changed (1) hide show
  1. modules/code_formatter.py +700 -0
modules/code_formatter.py CHANGED
@@ -0,0 +1,700 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Code Formatter & Syntax Highlighter Module for AumCore AI
3
+ Version: 1.0.0
4
+ Author: AumCore AI
5
+ Location: /app/modules/code_formatter.py
6
+ """
7
+
8
+ import re
9
+ import json
10
+ import html
11
+ from typing import Dict, List, Optional, Tuple, Any
12
+ from enum import Enum
13
+ from dataclasses import dataclass, field
14
+ from fastapi import APIRouter, HTTPException
15
+ from fastapi.responses import HTMLResponse, JSONResponse
16
+ import pygments
17
+ from pygments import lexers, formatters, styles
18
+ from pygments.lexers import (
19
+ PythonLexer, JavascriptLexer, HtmlLexer, CssLexer,
20
+ SqlLexer, JavaLexer, CLexer, CppLexer, GoLexer,
21
+ RustLexer, PhpLexer, RubyLexer, SwiftLexer
22
+ )
23
+ import base64
24
+ import uuid
25
+ from datetime import datetime
26
+ import os
27
+
28
+ class CodeLanguage(Enum):
29
+ """Supported programming languages for formatting"""
30
+ PYTHON = "python"
31
+ JAVASCRIPT = "javascript"
32
+ HTML = "html"
33
+ CSS = "css"
34
+ SQL = "sql"
35
+ JAVA = "java"
36
+ C = "c"
37
+ CPP = "cpp"
38
+ GO = "go"
39
+ RUST = "rust"
40
+ PHP = "php"
41
+ RUBY = "ruby"
42
+ SWIFT = "swift"
43
+ JSON = "json"
44
+ YAML = "yaml"
45
+ MARKDOWN = "markdown"
46
+ BASH = "bash"
47
+
48
+ class CodeTheme(Enum):
49
+ """Available color themes"""
50
+ MONOKAI = "monokai"
51
+ VSCODE = "vscode"
52
+ SOLARIZED = "solarized"
53
+ DRACULA = "dracula"
54
+ GITHUB = "github"
55
+ VS = "vs"
56
+ XCODE = "xcode"
57
+
58
+ @dataclass
59
+ class FormatOptions:
60
+ """Code formatting options"""
61
+ theme: CodeTheme = CodeTheme.MONOKAI
62
+ show_line_numbers: bool = True
63
+ show_copy_button: bool = True
64
+ show_download_button: bool = True
65
+ language_label: bool = True
66
+ max_height: Optional[str] = "500px"
67
+ border_radius: str = "8px"
68
+ font_family: str = "'Fira Code', 'Consolas', monospace"
69
+ font_size: str = "14px"
70
+
71
+ class AumCoreCodeFormatter:
72
+ """
73
+ Advanced Code Formatter with Syntax Highlighting
74
+ Creates beautiful, interactive code blocks
75
+ """
76
+
77
+ def __init__(self):
78
+ self._lexer_map = self._create_lexer_map()
79
+ self._theme_styles = self._load_theme_styles()
80
+ self._code_cache: Dict[str, str] = {}
81
+
82
+ def _create_lexer_map(self) -> Dict[str, Any]:
83
+ """Create mapping from language names to Pygments lexers"""
84
+ return {
85
+ "python": PythonLexer(),
86
+ "javascript": JavascriptLexer(),
87
+ "js": JavascriptLexer(),
88
+ "html": HtmlLexer(),
89
+ "css": CssLexer(),
90
+ "sql": SqlLexer(),
91
+ "java": JavaLexer(),
92
+ "c": CLexer(),
93
+ "cpp": CppLexer(),
94
+ "c++": CppLexer(),
95
+ "go": GoLexer(),
96
+ "rust": RustLexer(),
97
+ "php": PhpLexer(),
98
+ "ruby": RubyLexer(),
99
+ "swift": SwiftLexer(),
100
+ "json": lexers.JsonLexer(),
101
+ "yaml": lexers.YamlLexer(),
102
+ "markdown": lexers.MarkdownLexer(),
103
+ "bash": lexers.BashLexer(),
104
+ "shell": lexers.BashLexer(),
105
+ "sh": lexers.BashLexer(),
106
+ }
107
+
108
+ def _load_theme_styles(self) -> Dict[str, Dict]:
109
+ """Load color styles for different themes"""
110
+ return {
111
+ "monokai": {
112
+ "background": "#272822",
113
+ "foreground": "#f8f8f2",
114
+ "line_numbers": "#75715e",
115
+ "line_numbers_bg": "#272822",
116
+ "border": "#49483e",
117
+ "button_bg": "#49483e",
118
+ "button_hover": "#5a594e",
119
+ "button_text": "#f8f8f2",
120
+ "header_bg": "#49483e",
121
+ "header_text": "#f8f8f2",
122
+ "keyword": "#f92672",
123
+ "string": "#e6db74",
124
+ "comment": "#75715e",
125
+ "number": "#ae81ff",
126
+ "function": "#a6e22e",
127
+ "class": "#a6e22e",
128
+ "operator": "#f8f8f2",
129
+ },
130
+ "vscode": {
131
+ "background": "#1e1e1e",
132
+ "foreground": "#d4d4d4",
133
+ "line_numbers": "#858585",
134
+ "line_numbers_bg": "#1e1e1e",
135
+ "border": "#252526",
136
+ "button_bg": "#007acc",
137
+ "button_hover": "#1a8cff",
138
+ "button_text": "#ffffff",
139
+ "header_bg": "#252526",
140
+ "header_text": "#cccccc",
141
+ "keyword": "#569cd6",
142
+ "string": "#ce9178",
143
+ "comment": "#6a9955",
144
+ "number": "#b5cea8",
145
+ "function": "#dcdcaa",
146
+ "class": "#4ec9b0",
147
+ "operator": "#d4d4d4",
148
+ },
149
+ "github": {
150
+ "background": "#f6f8fa",
151
+ "foreground": "#24292e",
152
+ "line_numbers": "#6a737d",
153
+ "line_numbers_bg": "#f6f8fa",
154
+ "border": "#e1e4e8",
155
+ "button_bg": "#0366d6",
156
+ "button_hover": "#005cc5",
157
+ "button_text": "#ffffff",
158
+ "header_bg": "#f6f8fa",
159
+ "header_text": "#24292e",
160
+ "keyword": "#d73a49",
161
+ "string": "#032f62",
162
+ "comment": "#6a737d",
163
+ "number": "#005cc5",
164
+ "function": "#6f42c1",
165
+ "class": "#22863a",
166
+ "operator": "#24292e",
167
+ }
168
+ }
169
+
170
+ def detect_language(self, code: str, hint: str = None) -> str:
171
+ """
172
+ Detect programming language from code
173
+
174
+ Args:
175
+ code: Source code
176
+ hint: Optional language hint
177
+
178
+ Returns:
179
+ Detected language name
180
+ """
181
+ if hint and hint.lower() in self._lexer_map:
182
+ return hint.lower()
183
+
184
+ # Try to auto-detect
185
+ try:
186
+ lexer = lexers.guess_lexer(code)
187
+ for lang_name, lexer_obj in self._lexer_map.items():
188
+ if isinstance(lexer, type(lexer_obj)):
189
+ return lang_name
190
+ except:
191
+ pass
192
+
193
+ # Fallback based on code patterns
194
+ code_lower = code.lower()
195
+
196
+ if re.search(r'def\s+\w+\(|import\s+\w+|from\s+\w+', code_lower):
197
+ return "python"
198
+ elif re.search(r'function\s+\w+|const\s+\w+=|let\s+\w+=', code_lower):
199
+ return "javascript"
200
+ elif re.search(r'<html|<head|<body|<!DOCTYPE', code_lower):
201
+ return "html"
202
+ elif re.search(r'\.\s*{|\s*:\s*|\s*;\s*$', code_lower):
203
+ return "css"
204
+ elif re.search(r'SELECT\s+|INSERT\s+|UPDATE\s+|CREATE\s+TABLE', code_lower, re.IGNORECASE):
205
+ return "sql"
206
+ elif re.search(r'public\s+class|private\s+\w+|System\.out\.', code_lower):
207
+ return "java"
208
+
209
+ return "python" # Default
210
+
211
+ def format_code_html(self,
212
+ code: str,
213
+ language: str = None,
214
+ options: FormatOptions = None) -> str:
215
+ """
216
+ Format code as HTML with syntax highlighting
217
+
218
+ Args:
219
+ code: Source code to format
220
+ language: Programming language (auto-detected if None)
221
+ options: Formatting options
222
+
223
+ Returns:
224
+ HTML code block
225
+ """
226
+ options = options or FormatOptions()
227
+
228
+ # Detect language if not provided
229
+ lang = language.lower() if language else self.detect_language(code)
230
+ if lang not in self._lexer_map:
231
+ lang = "python" # Default fallback
232
+
233
+ # Generate unique ID for this code block
234
+ block_id = f"codeblock-{uuid.uuid4().hex[:8]}"
235
+
236
+ # Get theme colors
237
+ theme_name = options.theme.value
238
+ theme = self._theme_styles.get(theme_name, self._theme_styles["monokai"])
239
+
240
+ # Generate highlighted code
241
+ highlighted_code = self._highlight_code(code, lang, theme_name)
242
+
243
+ # Build HTML
244
+ html_output = self._build_code_block_html(
245
+ code=highlighted_code,
246
+ raw_code=code,
247
+ language=lang,
248
+ block_id=block_id,
249
+ theme=theme,
250
+ options=options
251
+ )
252
+
253
+ # Cache for potential reuse
254
+ cache_key = f"{lang}:{hash(code)}"
255
+ self._code_cache[cache_key] = html_output
256
+
257
+ return html_output
258
+
259
+ def _highlight_code(self, code: str, language: str, theme: str) -> str:
260
+ """
261
+ Highlight code using Pygments
262
+
263
+ Args:
264
+ code: Source code
265
+ language: Programming language
266
+ theme: Color theme
267
+
268
+ Returns:
269
+ HTML with syntax highlighting
270
+ """
271
+ try:
272
+ lexer = self._lexer_map.get(language, PythonLexer())
273
+
274
+ # Use appropriate formatter
275
+ if theme == "monokai":
276
+ style = styles.get_style_by_name("monokai")
277
+ elif theme == "solarized":
278
+ style = styles.get_style_by_name("solarized-dark")
279
+ else:
280
+ style = styles.get_style_by_name("default")
281
+
282
+ formatter = formatters.HtmlFormatter(
283
+ style=style,
284
+ linenos=False,
285
+ cssclass="",
286
+ noclasses=False,
287
+ prestyles="margin: 0;"
288
+ )
289
+
290
+ highlighted = pygments.highlight(code, lexer, formatter)
291
+
292
+ # Extract just the <pre><code> part
293
+ highlighted = highlighted.replace('class="highlight"', '')
294
+ highlighted = highlighted.replace('<pre>', '').replace('</pre>', '')
295
+ highlighted = highlighted.replace('<div class="highlight">', '').replace('</div>', '')
296
+
297
+ return highlighted.strip()
298
+
299
+ except Exception as e:
300
+ # Fallback: basic HTML escape
301
+ return f'<code class="language-{language}">{html.escape(code)}</code>'
302
+
303
+ def _build_code_block_html(self,
304
+ code: str,
305
+ raw_code: str,
306
+ language: str,
307
+ block_id: str,
308
+ theme: Dict,
309
+ options: FormatOptions) -> str:
310
+ """
311
+ Build complete code block HTML with controls
312
+
313
+ Args:
314
+ code: Highlighted code HTML
315
+ raw_code: Original raw code
316
+ language: Programming language
317
+ block_id: Unique block ID
318
+ theme: Theme colors dictionary
319
+ options: Formatting options
320
+
321
+ Returns:
322
+ Complete HTML code block
323
+ """
324
+ # Language display name
325
+ lang_display = language.upper() if language == "python" else language.title()
326
+
327
+ # Line numbers HTML
328
+ line_numbers = ""
329
+ if options.show_line_numbers:
330
+ lines = raw_code.split('\n')
331
+ line_numbers_html = []
332
+ for i in range(1, len(lines) + 1):
333
+ line_numbers_html.append(f'<span class="line-number">{i}</span>')
334
+ line_numbers = f'<div class="line-numbers">{chr(10).join(line_numbers_html)}</div>'
335
+
336
+ # Buttons HTML
337
+ buttons_html = ""
338
+ if options.show_copy_button or options.show_download_button:
339
+ buttons = []
340
+
341
+ if options.show_copy_button:
342
+ buttons.append(f'''
343
+ <button class="copy-btn" onclick="copyCode('{block_id}')"
344
+ title="Copy to clipboard">
345
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
346
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
347
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
348
+ </svg>
349
+ Copy
350
+ </button>
351
+ ''')
352
+
353
+ if options.show_download_button:
354
+ # Encode code for download
355
+ encoded_code = base64.b64encode(raw_code.encode()).decode()
356
+ filename = f"code_{language}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.{language}"
357
+
358
+ buttons.append(f'''
359
+ <a href="data:text/plain;base64,{encoded_code}"
360
+ download="{filename}"
361
+ class="download-btn"
362
+ title="Download code">
363
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
364
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
365
+ <polyline points="7 10 12 15 17 10"></polyline>
366
+ <line x1="12" y1="15" x2="12" y2="3"></line>
367
+ </svg>
368
+ Download
369
+ </a>
370
+ ''')
371
+
372
+ if buttons:
373
+ buttons_html = f'<div class="code-actions">{chr(10).join(buttons)}</div>'
374
+
375
+ # Header HTML
376
+ header_html = ""
377
+ if options.language_label or buttons_html:
378
+ header_parts = []
379
+
380
+ if options.language_label:
381
+ header_parts.append(f'<div class="language-label">{lang_display}</div>')
382
+
383
+ if buttons_html:
384
+ header_parts.append(buttons_html)
385
+
386
+ header_html = f'<div class="code-header">{chr(10).join(header_parts)}</div>'
387
+
388
+ # CSS Styles
389
+ css_styles = f'''
390
+ <style>
391
+ #{block_id} {{
392
+ background: {theme['background']};
393
+ color: {theme['foreground']};
394
+ border: 1px solid {theme['border']};
395
+ border-radius: {options.border_radius};
396
+ font-family: {options.font_family};
397
+ font-size: {options.font_size};
398
+ overflow: hidden;
399
+ margin: 1rem 0;
400
+ }}
401
+
402
+ #{block_id} .code-header {{
403
+ background: {theme['header_bg']};
404
+ color: {theme['header_text']};
405
+ padding: 10px 15px;
406
+ display: flex;
407
+ justify-content: space-between;
408
+ align-items: center;
409
+ border-bottom: 1px solid {theme['border']};
410
+ }}
411
+
412
+ #{block_id} .language-label {{
413
+ font-family: {options.font_family};
414
+ font-weight: 600;
415
+ font-size: 13px;
416
+ display: flex;
417
+ align-items: center;
418
+ gap: 8px;
419
+ }}
420
+
421
+ #{block_id} .language-label::before {{
422
+ content: "✦";
423
+ color: {theme['function']};
424
+ font-size: 12px;
425
+ }}
426
+
427
+ #{block_id} .code-actions {{
428
+ display: flex;
429
+ gap: 8px;
430
+ }}
431
+
432
+ #{block_id} .copy-btn, #{block_id} .download-btn {{
433
+ background: {theme['button_bg']};
434
+ color: {theme['button_text']};
435
+ border: none;
436
+ padding: 6px 12px;
437
+ border-radius: 4px;
438
+ cursor: pointer;
439
+ font-size: 13px;
440
+ font-family: 'Inter', sans-serif;
441
+ font-weight: 500;
442
+ display: flex;
443
+ align-items: center;
444
+ gap: 6px;
445
+ transition: all 0.2s ease;
446
+ text-decoration: none;
447
+ }}
448
+
449
+ #{block_id} .copy-btn:hover, #{block_id} .download-btn:hover {{
450
+ background: {theme['button_hover']};
451
+ transform: translateY(-1px);
452
+ }}
453
+
454
+ #{block_id} .copy-btn:active, #{block_id} .download-btn:active {{
455
+ transform: translateY(0);
456
+ }}
457
+
458
+ #{block_id} .copy-btn.copied {{
459
+ background: #10b981;
460
+ }}
461
+
462
+ #{block_id} .code-container {{
463
+ display: flex;
464
+ overflow: auto;
465
+ max-height: {options.max_height or 'none'};
466
+ }}
467
+
468
+ #{block_id} .line-numbers {{
469
+ background: {theme['line_numbers_bg']};
470
+ color: {theme['line_numbers']};
471
+ padding: 15px 10px;
472
+ text-align: right;
473
+ user-select: none;
474
+ border-right: 1px solid {theme['border']};
475
+ font-family: {options.font_family};
476
+ font-size: {options.font_size};
477
+ line-height: 1.5;
478
+ }}
479
+
480
+ #{block_id} .line-number {{
481
+ display: block;
482
+ padding: 0 5px;
483
+ }}
484
+
485
+ #{block_id} .code-content {{
486
+ flex: 1;
487
+ padding: 15px;
488
+ overflow-x: auto;
489
+ line-height: 1.5;
490
+ }}
491
+
492
+ #{block_id} pre {{
493
+ margin: 0;
494
+ padding: 0;
495
+ background: transparent;
496
+ font-family: inherit;
497
+ font-size: inherit;
498
+ }}
499
+
500
+ #{block_id} code {{
501
+ font-family: inherit;
502
+ font-size: inherit;
503
+ background: transparent;
504
+ }}
505
+
506
+ /* Syntax highlighting colors */
507
+ #{block_id} .highlight .k {{ color: {theme['keyword']}; }} /* Keyword */
508
+ #{block_id} .highlight .s {{ color: {theme['string']}; }} /* String */
509
+ #{block_id} .highlight .c {{ color: {theme['comment']}; }} /* Comment */
510
+ #{block_id} .highlight .m {{ color: {theme['number']}; }} /* Number */
511
+ #{block_id} .highlight .nf {{ color: {theme['function']}; }} /* Function */
512
+ #{block_id} .highlight .nc {{ color: {theme['class']}; }} /* Class */
513
+ #{block_id} .highlight .o {{ color: {theme['operator']}; }} /* Operator */
514
+ </style>
515
+ '''
516
+
517
+ # JavaScript for copy functionality
518
+ js_script = f'''
519
+ <script>
520
+ function copyCode(blockId) {{
521
+ const block = document.getElementById(blockId);
522
+ const codeElement = block.querySelector('.code-content code');
523
+ const codeText = codeElement ? codeElement.textContent : '';
524
+ const rawCode = `{html.escape(raw_code)}`;
525
+
526
+ navigator.clipboard.writeText(rawCode).then(() => {{
527
+ const copyBtn = block.querySelector('.copy-btn');
528
+ if (copyBtn) {{
529
+ const originalHTML = copyBtn.innerHTML;
530
+ const originalClass = copyBtn.className;
531
+
532
+ copyBtn.innerHTML = `
533
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
534
+ <polyline points="20 6 9 17 4 12"></polyline>
535
+ </svg>
536
+ Copied!
537
+ `;
538
+ copyBtn.className = 'copy-btn copied';
539
+
540
+ setTimeout(() => {{
541
+ copyBtn.innerHTML = originalHTML;
542
+ copyBtn.className = originalClass;
543
+ }}, 2000);
544
+ }}
545
+ }}).catch(err => {{
546
+ console.error('Copy failed:', err);
547
+ const copyBtn = block.querySelector('.copy-btn');
548
+ if (copyBtn) {{
549
+ copyBtn.innerHTML = `
550
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
551
+ <line x1="18" y1="6" x2="6" y2="18"></line>
552
+ <line x1="6" y1="6" x2="18" y2="18"></line>
553
+ </svg>
554
+ Failed
555
+ `;
556
+ setTimeout(() => {{
557
+ copyBtn.innerHTML = `
558
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
559
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
560
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
561
+ </svg>
562
+ Copy
563
+ `;
564
+ }}, 2000);
565
+ }}
566
+ }});
567
+ }}
568
+ </script>
569
+ '''
570
+
571
+ # Build final HTML
572
+ html_structure = f'''
573
+ <div id="{block_id}" class="code-block">
574
+ {css_styles}
575
+ {header_html}
576
+ <div class="code-container">
577
+ {line_numbers}
578
+ <div class="code-content">
579
+ <pre>{code}</pre>
580
+ </div>
581
+ </div>
582
+ {js_script}
583
+ </div>
584
+ '''
585
+
586
+ return html_structure.strip()
587
+
588
+ def format_multiple_codes(self, code_blocks: List[Dict], options: FormatOptions = None) -> str:
589
+ """
590
+ Format multiple code blocks at once
591
+
592
+ Args:
593
+ code_blocks: List of dicts with 'code' and optional 'language'
594
+ options: Formatting options
595
+
596
+ Returns:
597
+ Combined HTML with all code blocks
598
+ """
599
+ html_blocks = []
600
+
601
+ for i, block in enumerate(code_blocks):
602
+ code = block.get('code', '')
603
+ language = block.get('language')
604
+
605
+ if code.strip():
606
+ html_block = self.format_code_html(code, language, options)
607
+ html_blocks.append(html_block)
608
+
609
+ return '\n'.join(html_blocks)
610
+
611
+ # Global instance
612
+ code_formatter = AumCoreCodeFormatter()
613
+
614
+ # FastAPI Router for module registration
615
+ def register_module(app, client, username):
616
+ """Register code formatter module with FastAPI app"""
617
+ router = APIRouter()
618
+
619
+ @router.get("/code/format")
620
+ async def format_code_endpoint(
621
+ code: str,
622
+ language: str = None,
623
+ theme: str = "monokai",
624
+ line_numbers: bool = True,
625
+ copy_button: bool = True,
626
+ download_button: bool = True
627
+ ):
628
+ """API endpoint to format code"""
629
+ try:
630
+ options = FormatOptions(
631
+ theme=CodeTheme(theme) if theme in [t.value for t in CodeTheme] else CodeTheme.MONOKAI,
632
+ show_line_numbers=line_numbers,
633
+ show_copy_button=copy_button,
634
+ show_download_button=download_button
635
+ )
636
+
637
+ html_output = code_formatter.format_code_html(code, language, options)
638
+
639
+ return HTMLResponse(content=html_output)
640
+
641
+ except Exception as e:
642
+ raise HTTPException(status_code=400, detail=f"Formatting error: {str(e)}")
643
+
644
+ @router.get("/code/detect")
645
+ async def detect_language_endpoint(code: str):
646
+ """API endpoint to detect programming language"""
647
+ try:
648
+ language = code_formatter.detect_language(code)
649
+ return {"language": language, "success": True}
650
+ except Exception as e:
651
+ return {"success": False, "error": str(e)}
652
+
653
+ @router.post("/code/format/batch")
654
+ async def format_batch_endpoint(code_blocks: List[Dict]):
655
+ """API endpoint to format multiple code blocks"""
656
+ try:
657
+ options = FormatOptions()
658
+ html_output = code_formatter.format_multiple_codes(code_blocks, options)
659
+ return HTMLResponse(content=html_output)
660
+ except Exception as e:
661
+ raise HTTPException(status_code=400, detail=f"Batch formatting error: {str(e)}")
662
+
663
+ # Helper function for direct use in other modules
664
+ @router.get("/code/formatter/status")
665
+ async def formatter_status():
666
+ return {
667
+ "module": "code_formatter",
668
+ "status": "active",
669
+ "version": "1.0.0",
670
+ "languages": list(code_formatter._lexer_map.keys()),
671
+ "themes": [theme.value for theme in CodeTheme]
672
+ }
673
+
674
+ app.include_router(router)
675
+ print("✅ Code Formatter module registered with FastAPI")
676
+
677
+ # Helper functions for easy import
678
+ def format_code_html(code: str, language: str = None, theme: str = "monokai") -> str:
679
+ """Format code as HTML with syntax highlighting"""
680
+ options = FormatOptions(theme=CodeTheme(theme) if theme in [t.value for t in CodeTheme] else CodeTheme.MONOKAI)
681
+ return code_formatter.format_code_html(code, language, options)
682
+
683
+ def detect_code_language(code: str) -> str:
684
+ """Detect programming language of code"""
685
+ return code_formatter.detect_language(code)
686
+
687
+ # Module exports
688
+ __all__ = [
689
+ 'AumCoreCodeFormatter',
690
+ 'CodeLanguage',
691
+ 'CodeTheme',
692
+ 'FormatOptions',
693
+ 'code_formatter',
694
+ 'format_code_html',
695
+ 'detect_code_language',
696
+ 'register_module'
697
+ ]
698
+
699
+ __version__ = "1.0.0"
700
+ __author__ = "AumCore AI"