From 30156458ba70edb5d308595b59d7ab3ad7c3c32e Mon Sep 17 00:00:00 2001 From: gaoziman <2942894660@qq.com> Date: Sun, 21 Dec 2025 16:34:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E4=BB=A3=E7=A0=81=E6=89=A7=E8=A1=8C):=20?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E8=BF=9C=E7=A8=8B=E5=BC=95=E6=93=8E=E5=A4=9A?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=20Unicode=20=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 改进错误信息处理,提供更清晰的错误提示 - 添加 Go 语言非 ASCII 字符转义支持 - 添加 Kotlin、C#、C/C++ 标准 Unicode 转义 - 添加 Rust、Swift 大括号格式转义 \u{XXXX} - 添加 Ruby、PHP 字符串转义支持 --- src/lib/code-runner/engines/remote.ts | 247 +++++++++++++++++++++++++- 1 file changed, 244 insertions(+), 3 deletions(-) diff --git a/src/lib/code-runner/engines/remote.ts b/src/lib/code-runner/engines/remote.ts index d49706e..bb18fb2 100644 --- a/src/lib/code-runner/engines/remote.ts +++ b/src/lib/code-runner/engines/remote.ts @@ -110,14 +110,29 @@ class RemoteEngine implements IExecutionEngine { } // 处理运行结果 - const hasError = result.run.code !== 0 || result.run.signal !== null; + const runCode = result.run.code; + const hasError = runCode !== 0 || result.run.signal !== null; const output = this.formatOutput(result.run.stdout || result.run.output); const errorOutput = result.run.stderr ? this.formatOutput(result.run.stderr) : undefined; + // 构建错误信息 + let errorMessage: string | undefined; + if (hasError) { + if (errorOutput) { + errorMessage = errorOutput; + } else if (result.run.signal) { + errorMessage = `进程被信号终止: ${result.run.signal}`; + } else if (runCode === null || runCode === undefined) { + errorMessage = '执行异常: 未获取到退出代码'; + } else { + errorMessage = `退出代码: ${runCode}`; + } + } + return { success: !hasError || (output.length > 0 && !errorOutput), output: output.slice(0, MAX_OUTPUT_LENGTH), - error: hasError ? errorOutput || `退出代码: ${result.run.code}` : undefined, + error: errorMessage, executionTime, engine: 'remote', }; @@ -170,11 +185,51 @@ class RemoteEngine implements IExecutionEngine { private preprocessCode(code: string, language: string): string { const lang = language.toLowerCase(); - // 只对 Java 进行处理(Java 字符串支持 \uXXXX 转义) + // Java:\uXXXX 格式 + UTF-8 输出流注入 if (lang === 'java') { return this.escapeNonAsciiForJava(code); } + // Go:\uXXXX 格式 + if (lang === 'go' || lang === 'golang') { + return this.escapeNonAsciiForGo(code); + } + + // Kotlin:\uXXXX 格式(类似 Java,但不需要注入 UTF-8 设置) + if (lang === 'kotlin' || lang === 'kt') { + return this.escapeNonAsciiStandard(code); + } + + // C#:\uXXXX 格式 + if (lang === 'csharp' || lang === 'cs') { + return this.escapeNonAsciiStandard(code); + } + + // C/C++:\uXXXX 格式 (C11/C++11) + if (lang === 'c' || lang === 'cpp' || lang === 'c++') { + return this.escapeNonAsciiStandard(code); + } + + // Rust:\u{XXXX} 格式(大括号语法) + if (lang === 'rust' || lang === 'rs') { + return this.escapeNonAsciiBraceFormat(code); + } + + // Swift:\u{XXXX} 格式(大括号语法) + if (lang === 'swift') { + return this.escapeNonAsciiBraceFormat(code); + } + + // Ruby:\u{XXXX} 格式(大括号语法) + if (lang === 'ruby' || lang === 'rb') { + return this.escapeNonAsciiRuby(code); + } + + // PHP:\u{XXXX} 格式(PHP 7+,仅双引号字符串) + if (lang === 'php') { + return this.escapeNonAsciiPHP(code); + } + return code; } @@ -234,6 +289,85 @@ class RemoteEngine implements IExecutionEngine { return quote + escaped + quote; } + /** + * 将 Go 代码中字符串内的非 ASCII 字符转换为 Unicode 转义序列 + */ + private escapeNonAsciiForGo(code: string): string { + // 处理双引号字符串 + let result = code.replace(/"([^"\\]|\\.)*"/g, (match) => { + return this.escapeGoStringContent(match); + }); + + // 处理反引号字符串(Go 的原始字符串,不支持转义,需要特殊处理) + // 对于反引号字符串,我们将其转换为双引号字符串并添加转义 + result = result.replace(/`([^`]*)`/g, (match, content) => { + // 检查是否包含非 ASCII 字符 + if (/[^\x00-\x7F]/.test(content)) { + // 转换为双引号字符串,并转义非 ASCII 字符 + let escaped = ''; + for (let i = 0; i < content.length; i++) { + const char = content[i]; + const code = char.charCodeAt(0); + + if (char === '"') { + escaped += '\\"'; + } else if (char === '\\') { + escaped += '\\\\'; + } else if (char === '\n') { + escaped += '\\n'; + } else if (char === '\r') { + escaped += '\\r'; + } else if (char === '\t') { + escaped += '\\t'; + } else if (code > 127) { + escaped += '\\u' + code.toString(16).padStart(4, '0'); + } else { + escaped += char; + } + } + return '"' + escaped + '"'; + } + return match; + }); + + return result; + } + + /** + * 转义 Go 字符串内容中的非 ASCII 字符 + */ + private escapeGoStringContent(str: string): string { + const content = str.slice(1, -1); // 移除引号 + let escaped = ''; + + for (let i = 0; i < content.length; i++) { + const char = content[i]; + const code = char.charCodeAt(0); + + // 处理转义序列(保留原样) + if (char === '\\' && i + 1 < content.length) { + escaped += char + content[i + 1]; + i++; + continue; + } + + // 非 ASCII 字符转换为 \uXXXX 或 \UXXXXXXXX + if (code > 127) { + // 对于 BMP 范围内的字符使用 \uXXXX + if (code <= 0xFFFF) { + escaped += '\\u' + code.toString(16).padStart(4, '0'); + } else { + // 超出 BMP 的字符使用 \UXXXXXXXX + escaped += '\\U' + code.toString(16).padStart(8, '0'); + } + } else { + escaped += char; + } + } + + return '"' + escaped + '"'; + } + private formatOutput(output: string, prefix?: string): string { let result = output.trim(); @@ -246,6 +380,113 @@ class RemoteEngine implements IExecutionEngine { return result; } + + /** + * 标准 \uXXXX 格式转义(用于 Kotlin, C#, C/C++) + * 只处理双引号字符串 + */ + private escapeNonAsciiStandard(code: string): string { + return code.replace(/"([^"\\]|\\.)*"/g, (match) => { + return this.escapeStringContent(match, '"'); + }); + } + + /** + * 大括号格式转义 \u{XXXX}(用于 Rust, Swift) + */ + private escapeNonAsciiBraceFormat(code: string): string { + return code.replace(/"([^"\\]|\\.)*"/g, (match) => { + const content = match.slice(1, -1); + let escaped = ''; + + for (let i = 0; i < content.length; i++) { + const char = content[i]; + const charCode = char.charCodeAt(0); + + // 处理转义序列(保留原样) + if (char === '\\' && i + 1 < content.length) { + escaped += char + content[i + 1]; + i++; + continue; + } + + // 非 ASCII 字符转换为 \u{XXXX} + if (charCode > 127) { + escaped += '\\u{' + charCode.toString(16) + '}'; + } else { + escaped += char; + } + } + + return '"' + escaped + '"'; + }); + } + + /** + * Ruby 字符串转义 + * 双引号字符串支持 \u{XXXX},单引号字符串不支持转义 + */ + private escapeNonAsciiRuby(code: string): string { + // 只处理双引号字符串 + return code.replace(/"([^"\\]|\\.)*"/g, (match) => { + const content = match.slice(1, -1); + let escaped = ''; + + for (let i = 0; i < content.length; i++) { + const char = content[i]; + const charCode = char.charCodeAt(0); + + // 处理转义序列(保留原样) + if (char === '\\' && i + 1 < content.length) { + escaped += char + content[i + 1]; + i++; + continue; + } + + // 非 ASCII 字符转换为 \u{XXXX} + if (charCode > 127) { + escaped += '\\u{' + charCode.toString(16).padStart(4, '0') + '}'; + } else { + escaped += char; + } + } + + return '"' + escaped + '"'; + }); + } + + /** + * PHP 字符串转义 + * 只有双引号字符串支持 \u{XXXX},单引号字符串不处理 + */ + private escapeNonAsciiPHP(code: string): string { + // 只处理双引号字符串 + return code.replace(/"([^"\\]|\\.)*"/g, (match) => { + const content = match.slice(1, -1); + let escaped = ''; + + for (let i = 0; i < content.length; i++) { + const char = content[i]; + const charCode = char.charCodeAt(0); + + // 处理转义序列(保留原样) + if (char === '\\' && i + 1 < content.length) { + escaped += char + content[i + 1]; + i++; + continue; + } + + // 非 ASCII 字符转换为 \u{XXXX} + if (charCode > 127) { + escaped += '\\u{' + charCode.toString(16) + '}'; + } else { + escaped += char; + } + } + + return '"' + escaped + '"'; + }); + } } // 导出单例