From d30081b3a4432d16f0538b0c07a983f2f3e9b931 Mon Sep 17 00:00:00 2001 From: toucheres <761844639@qq.com> Date: Mon, 23 Mar 2026 21:27:22 +0800 Subject: [PATCH 1/4] make the vscode listen vscode-build.log (and xmake.conf) --- .gitignore | 1 + package-lock.json | 1 + src/xmake.ts | 5 +++-- yarn.lock | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 3e06df1..fdd2eea 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules .DS_Store *.swp *.swo +project.lock diff --git a/package-lock.json b/package-lock.json index 7cd4f57..7e38158 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "devDependencies": { "@types/node": "^16.10.4", "@types/vscode": "^1.58.0", + "rimraf": "^3.0.2", "typescript": "^4.4.3", "vsce": "^2.11.0" }, diff --git a/src/xmake.ts b/src/xmake.ts index 5a463f9..af1fefc 100644 --- a/src/xmake.ts +++ b/src/xmake.ts @@ -264,15 +264,16 @@ export class XMake implements vscode.Disposable { // init watcher async initWatcher() { + const workingDirectoryUri = vscode.Uri.file(config.workingDirectory); // init log file system watcher - this._logFileSystemWatcher = vscode.workspace.createFileSystemWatcher(".xmake/**/vscode-build.log"); + this._logFileSystemWatcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(workingDirectoryUri, ".xmake/**/vscode-build.log")); this._logFileSystemWatcher.onDidCreate(this.onLogFileUpdated.bind(this)); this._logFileSystemWatcher.onDidChange(this.onLogFileUpdated.bind(this)); this._logFileSystemWatcher.onDidDelete(this.onLogFileDeleted.bind(this)); // init config file system watcher - this._configFileSystemWatcher = vscode.workspace.createFileSystemWatcher(".xmake/**/xmake.conf"); + this._configFileSystemWatcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(workingDirectoryUri, ".xmake/**/xmake.conf")); this._configFileSystemWatcher.onDidCreate(this.onConfigFileUpdated.bind(this)); this._configFileSystemWatcher.onDidChange(this.onConfigFileUpdated.bind(this)); diff --git a/yarn.lock b/yarn.lock index 3b7ba72..4b4b9c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -553,7 +553,7 @@ readable-stream@^3.1.1, readable-stream@^3.4.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -rimraf@^3.0.0: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== From f55c647adb5ff2878ed6599a637a8ece5090d2e3 Mon Sep 17 00:00:00 2001 From: toucheres <761844639@qq.com> Date: Mon, 23 Mar 2026 21:50:12 +0800 Subject: [PATCH 2/4] improve the way of jugding log decodeing method --- src/problem.ts | 49 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/src/problem.ts b/src/problem.ts index 18c7cdc..2594b69 100644 --- a/src/problem.ts +++ b/src/problem.ts @@ -5,7 +5,6 @@ import * as vscode from 'vscode'; import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; -import * as encoding from 'encoding'; import {log} from './log'; import {config} from './config'; @@ -43,19 +42,53 @@ export class ProblemList implements vscode.Disposable { // exists logfile? if (logfile) { + // Judge the encoding method + const iconv = require("iconv-lite"); + + const isUtf8Bom = (buffer: Buffer) => buffer.length >= 3 && buffer[0] === 0xEF && buffer[1] === 0xBB && buffer[2] === 0xBF; + const isUtf16LeBom = (buffer: Buffer) => buffer.length >= 2 && buffer[0] === 0xFF && buffer[1] === 0xFE; + const isUtf16BeBom = (buffer: Buffer) => buffer.length >= 2 && buffer[0] === 0xFE && buffer[1] === 0xFF; + + const decodeWithConfidence = (buffer: Buffer): { encoding: string, text: string } => { + if (isUtf8Bom(buffer)) { + return { encoding: "utf8-bom", text: buffer.slice(3).toString("utf8") }; + } + if (isUtf16LeBom(buffer)) { + return { encoding: "utf16le-bom", text: iconv.decode(buffer.slice(2), "utf16le") }; + } + if (isUtf16BeBom(buffer)) { + return { encoding: "utf16be-bom", text: iconv.decode(buffer.slice(2), "utf16be") }; + } + + const utf8Text = buffer.toString("utf8"); + if (Buffer.from(utf8Text, "utf8").equals(buffer)) { + return { encoding: "utf8", text: utf8Text }; + } + + const gbkText = iconv.decode(buffer, "gbk"); + if (iconv.encode(gbkText, "gbk").equals(buffer)) { + return { encoding: "gbk", text: gbkText }; + } + + const utf8ReplacementCount = (utf8Text.match(/\uFFFD/g) || []).length; + const gbkReplacementCount = (gbkText.match(/\uFFFD/g) || []).length; + if (gbkReplacementCount < utf8ReplacementCount) { + return { encoding: "gbk-fallback", text: gbkText }; + } + + return { encoding: "utf8-fallback", text: utf8Text }; + }; + // on windows? const isWin = os.platform() == "win32"; // read the log file - fs.readFile(logfile, isWin? null : "utf8", (err, content) => { + fs.readFile(logfile, null, (err, content) => { if (!err && content) { - - // convert gbk to utf8 - let text = content; - if (isWin) { - text = encoding.convert(content, "utf8", "gbk").toString(); - } + const decoded = decodeWithConfidence(content); + const text = decoded.text; + // log.verbose(`diagnose logfile encoding: ${decoded.encoding}`); // init regex of gcc/clang output const rOutputGcc: RegExp = new RegExp("^(error: )?(.*?):([0-9]*):([0-9]*): (.*?): (.*)$"); From ff885fb2d5f6e3db7933e479f4c61d74295f917e Mon Sep 17 00:00:00 2001 From: toucheres <761844639@qq.com> Date: Tue, 24 Mar 2026 13:28:44 +0800 Subject: [PATCH 3/4] =?UTF-8?q?Buffer.slice=20=E2=86=92=20Buffer.subarray,?= =?UTF-8?q?=20move=20encoding=20judgement=20to=20utils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/problem.ts | 41 ++--------------------------------------- src/utils.ts | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/src/problem.ts b/src/problem.ts index 2594b69..6930e3b 100644 --- a/src/problem.ts +++ b/src/problem.ts @@ -7,6 +7,7 @@ import * as path from 'path'; import * as os from 'os'; import {log} from './log'; import {config} from './config'; +import {decodeBufferWithConfidence} from './utils'; // the problem list class export class ProblemList implements vscode.Disposable { @@ -41,44 +42,6 @@ export class ProblemList implements vscode.Disposable { // exists logfile? if (logfile) { - - // Judge the encoding method - const iconv = require("iconv-lite"); - - const isUtf8Bom = (buffer: Buffer) => buffer.length >= 3 && buffer[0] === 0xEF && buffer[1] === 0xBB && buffer[2] === 0xBF; - const isUtf16LeBom = (buffer: Buffer) => buffer.length >= 2 && buffer[0] === 0xFF && buffer[1] === 0xFE; - const isUtf16BeBom = (buffer: Buffer) => buffer.length >= 2 && buffer[0] === 0xFE && buffer[1] === 0xFF; - - const decodeWithConfidence = (buffer: Buffer): { encoding: string, text: string } => { - if (isUtf8Bom(buffer)) { - return { encoding: "utf8-bom", text: buffer.slice(3).toString("utf8") }; - } - if (isUtf16LeBom(buffer)) { - return { encoding: "utf16le-bom", text: iconv.decode(buffer.slice(2), "utf16le") }; - } - if (isUtf16BeBom(buffer)) { - return { encoding: "utf16be-bom", text: iconv.decode(buffer.slice(2), "utf16be") }; - } - - const utf8Text = buffer.toString("utf8"); - if (Buffer.from(utf8Text, "utf8").equals(buffer)) { - return { encoding: "utf8", text: utf8Text }; - } - - const gbkText = iconv.decode(buffer, "gbk"); - if (iconv.encode(gbkText, "gbk").equals(buffer)) { - return { encoding: "gbk", text: gbkText }; - } - - const utf8ReplacementCount = (utf8Text.match(/\uFFFD/g) || []).length; - const gbkReplacementCount = (gbkText.match(/\uFFFD/g) || []).length; - if (gbkReplacementCount < utf8ReplacementCount) { - return { encoding: "gbk-fallback", text: gbkText }; - } - - return { encoding: "utf8-fallback", text: utf8Text }; - }; - // on windows? const isWin = os.platform() == "win32"; @@ -86,7 +49,7 @@ export class ProblemList implements vscode.Disposable { fs.readFile(logfile, null, (err, content) => { if (!err && content) { - const decoded = decodeWithConfidence(content); + const decoded = decodeBufferWithConfidence(content); const text = decoded.text; // log.verbose(`diagnose logfile encoding: ${decoded.encoding}`); diff --git a/src/utils.ts b/src/utils.ts index 6d48539..d0cd164 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,6 +3,7 @@ // imports import * as vscode from 'vscode'; import * as path from 'path'; +import * as iconv from 'iconv-lite'; // get project root directory var projectRoot = null; @@ -79,6 +80,40 @@ export function replaceVars(str: string): string { return replacements.reduce((accdir, [needle, what]) => replaceAll(accdir, needle, what), str); } +export function decodeBufferWithConfidence(buffer: Buffer): { encoding: string; text: string } { + const isUtf8Bom = (value: Buffer) => value.length >= 3 && value[0] === 0xEF && value[1] === 0xBB && value[2] === 0xBF; + const isUtf16LeBom = (value: Buffer) => value.length >= 2 && value[0] === 0xFF && value[1] === 0xFE; + const isUtf16BeBom = (value: Buffer) => value.length >= 2 && value[0] === 0xFE && value[1] === 0xFF; + + if (isUtf8Bom(buffer)) { + return { encoding: 'utf8-bom', text: buffer.subarray(3).toString('utf8') }; + } + if (isUtf16LeBom(buffer)) { + return { encoding: 'utf16le-bom', text: iconv.decode(buffer.subarray(2), 'utf16le') }; + } + if (isUtf16BeBom(buffer)) { + return { encoding: 'utf16be-bom', text: iconv.decode(buffer.subarray(2), 'utf16be') }; + } + + const utf8Text = buffer.toString('utf8'); + if (Buffer.from(utf8Text, 'utf8').equals(buffer)) { + return { encoding: 'utf8', text: utf8Text }; + } + + const gbkText = iconv.decode(buffer, 'gbk'); + if (iconv.encode(gbkText, 'gbk').equals(buffer)) { + return { encoding: 'gbk', text: gbkText }; + } + + const utf8ReplacementCount = (utf8Text.match(/\uFFFD/g) || []).length; + const gbkReplacementCount = (gbkText.match(/\uFFFD/g) || []).length; + if (gbkReplacementCount < utf8ReplacementCount) { + return { encoding: 'gbk-fallback', text: gbkText }; + } + + return { encoding: 'utf8-fallback', text: utf8Text }; +} + // simplistic function for just checking if a string can be parsed as json export function isJson(text?: string): boolean { try { From aeddf9cee9f2cdae54bc2ea83e351c9f5ceca98a Mon Sep 17 00:00:00 2001 From: toucheres <761844639@qq.com> Date: Tue, 24 Mar 2026 21:27:18 +0800 Subject: [PATCH 4/4] rm the color and new sanitizeBuildLogText function to make the log file match with the terminal out --- src/problem.ts | 4 ++-- src/terminal.ts | 16 ++++++++++++++-- src/utils.ts | 17 +++++++++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/problem.ts b/src/problem.ts index 6930e3b..2b8a5e1 100644 --- a/src/problem.ts +++ b/src/problem.ts @@ -7,7 +7,7 @@ import * as path from 'path'; import * as os from 'os'; import {log} from './log'; import {config} from './config'; -import {decodeBufferWithConfidence} from './utils'; +import {decodeBufferWithConfidence, sanitizeBuildLogText} from './utils'; // the problem list class export class ProblemList implements vscode.Disposable { @@ -50,7 +50,7 @@ export class ProblemList implements vscode.Disposable { if (!err && content) { const decoded = decodeBufferWithConfidence(content); - const text = decoded.text; + const text = sanitizeBuildLogText(decoded.text); // log.verbose(`diagnose logfile encoding: ${decoded.encoding}`); // init regex of gcc/clang output diff --git a/src/terminal.ts b/src/terminal.ts index 9a27a0f..3afefbe 100644 --- a/src/terminal.ts +++ b/src/terminal.ts @@ -57,7 +57,13 @@ export class Terminal implements vscode.Disposable { var options = {"cwd": config.workingDirectory}; if (withlog) { - options["env"] = {XMAKE_LOGFILE: this.logfile}; + options["env"] = { + XMAKE_LOGFILE: this.logfile, + XMAKE_COLORTERM: "nocolor", + COLORTERM: "nocolor", + NO_COLOR: "1", + CLICOLOR: "0" + }; } const kind: vscode.TaskDefinition = { @@ -79,7 +85,13 @@ export class Terminal implements vscode.Disposable { var options = {"cwd": config.workingDirectory}; if (withlog) { - options["env"] = {XMAKE_LOGFILE: this.logfile}; + options["env"] = { + XMAKE_LOGFILE: this.logfile, + XMAKE_COLORTERM: "nocolor", + COLORTERM: "nocolor", + NO_COLOR: "1", + CLICOLOR: "0" + }; } const kind: vscode.TaskDefinition = { diff --git a/src/utils.ts b/src/utils.ts index d0cd164..f773774 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -114,6 +114,23 @@ export function decodeBufferWithConfidence(buffer: Buffer): { encoding: string; return { encoding: 'utf8-fallback', text: utf8Text }; } +// Remove terminal control bytes so diagnostics parser gets stable plain text. +export function sanitizeBuildLogText(text: string): string { + let cleaned = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + + // Strip ANSI escape sequences (e.g. colors, cursor controls). + cleaned = cleaned.replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, ''); + + // Apply backspaces to avoid broken words from in-place terminal updates. + while (/\x08/.test(cleaned)) { + cleaned = cleaned.replace(/[^\n]\x08/g, '').replace(/\x08/g, ''); + } + + // Remove other non-printable control chars but keep tab/newline. + cleaned = cleaned.replace(/[\x00-\x08\x0B-\x1F\x7F]/g, ''); + return cleaned; +} + // simplistic function for just checking if a string can be parsed as json export function isJson(text?: string): boolean { try {