sanket3280/code-execution
0
1/**2 * Code Formatter Service3 * Provides language-specific code formatting using industry-standard formatters4 */5 6const { exec } = require('child_process');7const { promisify } = require('util');8const execAsync = promisify(exec);9const fs = require('fs').promises;10const fsSync = require('fs');11const path = require('path');12const os = require('os');13 14class CodeFormatterService {15 /**16 * Format code based on language17 * @param {string} code - Raw code to format18 * @param {string} language - Programming language19 * @returns {Promise<string>} - Formatted code20 */21 async formatCode(code, language) {22 try {23 switch (language.toLowerCase()) {24 case 'javascript':25 case 'typescript':26 return await this.formatJavaScript(code);27 28 case 'python':29 return await this.formatPython(code);30 31 case 'java':32 return await this.formatJava(code);33 34 case 'cpp':35 case 'c':36 return await this.formatCpp(code);37 38 case 'csharp':39 return await this.formatCSharp(code);40 41 default:42 // Return original code if formatter not available43 return code;44 }45 } catch (error) {46 console.error(`Formatting error for ${language}:`, error.message);47 // Return original code on error48 return code;49 }50 }51 52 /**53 * Format JavaScript/TypeScript using Prettier (LeetCode Style - 4 spaces)54 */55 async formatJavaScript(code) {56 try {57 const prettier = require('prettier');58 59 // LeetCode-style JavaScript formatting with 4 spaces60 const formatted = await prettier.format(code, {61 parser: 'babel',62 tabWidth: 4,63 useTabs: false,64 semi: true,65 singleQuote: true,66 bracketSpacing: true,67 arrowParens: 'always',68 printWidth: 80,69 endOfLine: 'lf',70 });71 72 return formatted;73 } catch (error) {74 console.error('Prettier formatting error:', error.message);75 // Fallback to basic formatting76 return this.basicFormat(code);77 }78 }79 80 /**81 * Format Python using Black (via CLI) - ASYNC82 */83 async formatPython(code) {84 try {85 // Create temp file86 const tempFile = path.join(os.tmpdir(), `temp_${Date.now()}.py`);87 await fs.writeFile(tempFile, code, 'utf8');88 89 // Run black formatter (async, non-blocking)90 await execAsync(`black -q "${tempFile}"`, { 91 timeout: 500092 });93 94 // Read formatted code95 const formatted = await fs.readFile(tempFile, 'utf8');96 97 // Cleanup98 await fs.unlink(tempFile).catch(() => {});99 100 return formatted;101 } catch (error) {102 console.error('Black formatting error:', error.message);103 // Fallback to basic Python formatting104 return this.basicPythonFormat(code);105 }106 }107 108 /**109 * Format Java code110 */111 async formatJava(code) {112 try {113 // Basic Java formatting (can be enhanced with google-java-format)114 return this.basicJavaFormat(code);115 } catch (error) {116 console.error('Java formatting error:', error.message);117 return code;118 }119 }120 121 /**122 * Format C/C++ code - ASYNC123 */124 async formatCpp(code) {125 try {126 // Create temp file127 const tempFile = path.join(os.tmpdir(), `temp_${Date.now()}.cpp`);128 await fs.writeFile(tempFile, code, 'utf8');129 130 // Run clang-format if available (async, non-blocking)131 const { stdout } = await execAsync(`clang-format -style=Google "${tempFile}"`, {132 timeout: 5000133 });134 135 // Cleanup136 await fs.unlink(tempFile).catch(() => {});137 138 return stdout;139 } catch (error) {140 console.error('Clang-format error:', error.message);141 // Fallback to basic formatting142 return this.basicCppFormat(code);143 }144 }145 146 /**147 * Format C# code148 */149 async formatCSharp(code) {150 return this.basicCSharpFormat(code);151 }152 153 /**154 * Basic JavaScript formatting fallback (4 spaces - LeetCode style)155 */156 basicFormat(code) {157 let indentLevel = 0;158 const lines = code.split('\n');159 const result = [];160 161 for (let line of lines) {162 const trimmed = line.trim();163 164 // Decrease indent for closing braces165 if (trimmed.startsWith('}') || trimmed.startsWith(']') || trimmed.startsWith(')')) {166 indentLevel = Math.max(0, indentLevel - 1);167 }168 169 // Add indentation (4 spaces for JavaScript - LeetCode style)170 if (trimmed) {171 const indented = ' '.repeat(indentLevel) + trimmed;172 // Add space after commas and around operators173 const formatted = indented174 .replace(/,(\S)/g, ', $1') // Space after comma175 .replace(/([^=!<>])=([^=])/g, '$1 = $2') // Space around =176 .replace(/\s+$/g, ''); // Remove trailing whitespace177 result.push(formatted);178 } else {179 result.push('');180 }181 182 // Increase indent for opening braces183 if (trimmed.endsWith('{') || trimmed.endsWith('[') || trimmed.endsWith('(')) {184 indentLevel++;185 }186 }187 188 return result.join('\n');189 }190 191 /**192 * Basic Python formatting fallback (PEP8 style - LeetCode compatible)193 */194 basicPythonFormat(code) {195 const lines = code.split('\n');196 const result = [];197 let indentLevel = 0;198 let prevWasBlank = false;199 200 for (let i = 0; i < lines.length; i++) {201 const line = lines[i];202 const trimmed = line.trim();203 204 // Skip empty lines but track them205 if (!trimmed) {206 if (!prevWasBlank) {207 result.push('');208 prevWasBlank = true;209 }210 continue;211 }212 213 prevWasBlank = false;214 215 // Decrease indent for dedent keywords216 if (trimmed.startsWith('elif ') || 217 trimmed.startsWith('else:') || 218 trimmed.startsWith('except') || 219 trimmed.startsWith('finally:')) {220 indentLevel = Math.max(0, indentLevel - 1);221 }222 223 // Add indentation (4 spaces for Python - PEP8)224 const indentedLine = ' '.repeat(indentLevel) + trimmed;225 226 // Add space after commas if missing227 const formatted = indentedLine228 .replace(/,(\S)/g, ', $1') // Add space after comma229 .replace(/\s+$/g, ''); // Remove trailing whitespace230 231 result.push(formatted);232 233 // Increase indent after colon234 if (trimmed.endsWith(':')) {235 indentLevel++;236 }237 238 // Check if next line should dedent239 const nextLine = lines[i + 1];240 if (nextLine) {241 const nextTrimmed = nextLine.trim();242 if (nextTrimmed && !nextTrimmed.startsWith('elif') && 243 !nextTrimmed.startsWith('else') && 244 !nextTrimmed.startsWith('except') &&245 !nextTrimmed.startsWith('finally')) {246 // Check if current line is a single-line statement that should dedent247 if (trimmed.startsWith('return ') || 248 trimmed.startsWith('break') || 249 trimmed.startsWith('continue') ||250 trimmed === 'pass') {251 indentLevel = Math.max(0, indentLevel - 1);252 }253 }254 }255 }256 257 return result.join('\n');258 }259 260 /**261 * Basic Java formatting fallback (Google Java Style - LeetCode compatible)262 */263 basicJavaFormat(code) {264 let formatted = code;265 let indentLevel = 0;266 const lines = code.split('\n');267 const result = [];268 269 for (let line of lines) {270 const trimmed = line.trim();271 272 // Decrease indent for closing braces273 if (trimmed.startsWith('}')) {274 indentLevel = Math.max(0, indentLevel - 1);275 }276 277 // Add indentation (4 spaces for Java)278 if (trimmed) {279 const indented = ' '.repeat(indentLevel) + trimmed;280 // Add space after commas and around operators281 const formatted = indented282 .replace(/,(\S)/g, ', $1') // Space after comma283 .replace(/([^=!<>])=([^=])/g, '$1 = $2') // Space around =284 .replace(/\s+$/g, ''); // Remove trailing whitespace285 result.push(formatted);286 } else {287 result.push('');288 }289 290 // Increase indent for opening braces291 if (trimmed.endsWith('{')) {292 indentLevel++;293 }294 }295 296 return result.join('\n');297 }298 299 /**300 * Basic C++ formatting fallback (Google Style - LeetCode compatible)301 */302 basicCppFormat(code) {303 let indentLevel = 0;304 const lines = code.split('\n');305 const result = [];306 307 for (let line of lines) {308 const trimmed = line.trim();309 310 // Decrease indent for closing braces311 if (trimmed.startsWith('}')) {312 indentLevel = Math.max(0, indentLevel - 1);313 }314 315 // Add indentation (4 spaces for C++)316 if (trimmed) {317 const indented = ' '.repeat(indentLevel) + trimmed;318 // Add space after commas and around operators319 const formatted = indented320 .replace(/,(\S)/g, ', $1') // Space after comma321 .replace(/([^=!<>])=([^=])/g, '$1 = $2') // Space around =322 .replace(/\s+$/g, ''); // Remove trailing whitespace323 result.push(formatted);324 } else {325 result.push('');326 }327 328 // Increase indent for opening braces329 if (trimmed.endsWith('{')) {330 indentLevel++;331 }332 }333 334 return result.join('\n');335 }336 337 /**338 * Basic C# formatting fallback339 */340 basicCSharpFormat(code) {341 return this.basicFormat(code);342 }343}344 345module.exports = new CodeFormatterService();346 