From 70d7b98a24a9baac4c6132b148d2fffa72580b50 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 19:45:51 +0000 Subject: [PATCH 1/4] Initial plan From 61f74aca6a478bc300af32d853ca7e9d8dd418d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:42:20 +0000 Subject: [PATCH 2/4] Add support for single quotes in dryrun output parsing for mingw-make Co-authored-by: snehara99 <113148726+snehara99@users.noreply.github.com> --- src/parser.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/parser.ts b/src/parser.ts index 39495209..8b358939 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -385,9 +385,9 @@ async function parseLineAsTool( // - any spaces/tabs before the tool invocation // - with or without path (relative -to the makefile location- or full) // - with or without extension (windows only) - // - with or without quotes + // - with or without quotes (single or double, to support mingw-make) // - must have at least one space or tab after the tool invocation - let regexpStr: string = '^[\\s\\"]*(.*?)('; + let regexpStr: string = "^[\\s\\\"']*(.*?)("; if (process.platform === "win32") { regexpStr += versionedToolNames.join("\\.exe|"); @@ -399,7 +399,7 @@ async function parseLineAsTool( regexpStr += "|"; } - regexpStr += versionedToolNames.join("|") + ')(\\s|\\"\\s)(.*)$'; + regexpStr += versionedToolNames.join("|") + ')(\\s|[\\"\']\\s)(.*)$'; let regexp: RegExp = RegExp(regexpStr, "mg"); let match: RegExpExecArray | null = regexp.exec(line); From 90fc73885443a99e011eef665d88de4f8ee7784a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:45:49 +0000 Subject: [PATCH 3/4] Fix inconsistent string quote escaping for better code consistency Co-authored-by: snehara99 <113148726+snehara99@users.noreply.github.com> --- src/parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.ts b/src/parser.ts index 8b358939..0e33fcba 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -399,7 +399,7 @@ async function parseLineAsTool( regexpStr += "|"; } - regexpStr += versionedToolNames.join("|") + ')(\\s|[\\"\']\\s)(.*)$'; + regexpStr += versionedToolNames.join("|") + ")(\\s|[\\\"']\\s)(.*)$"; let regexp: RegExp = RegExp(regexpStr, "mg"); let match: RegExpExecArray | null = regexp.exec(line); From 6e7c5e71da36fbd4ad1af4a0349baf5f80bb0364 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 03:41:19 +0000 Subject: [PATCH 4/4] Add unit tests for single quote support in tool invocation parsing (issue #739) Co-authored-by: snehara99 <113148726+snehara99@users.noreply.github.com> --- src/test/fakeSuite/extension.test.ts | 106 +++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/src/test/fakeSuite/extension.test.ts b/src/test/fakeSuite/extension.test.ts index 5dfcb038..0f4044aa 100644 --- a/src/test/fakeSuite/extension.test.ts +++ b/src/test/fakeSuite/extension.test.ts @@ -130,6 +130,112 @@ suite("Unit testing replacing characters in and outside of quotes", () => { }); }); +// Unit test for the regex pattern used in parseLineAsTool to support +// both single and double quotes in tool invocations (issue #739: mingw-make support) +suite("Unit testing tool invocation regex pattern for single and double quotes", () => { + suiteSetup(async function (this: Mocha.Context) { + this.timeout(100000); + }); + + setup(async function (this: Mocha.Context) { + this.timeout(100000); + }); + + // Helper to build the regex pattern similar to parseLineAsTool in parser.ts + function buildToolInvocationRegex(toolNames: string[]): RegExp { + const prefixRegex = "(([a-zA-Z0-9-_.]*-)*"; + const suffixRegex = "(-[a-zA-Z0-9-_.]*)*)"; + const versionedToolNames = toolNames.map( + (t) => `${prefixRegex}${t}${suffixRegex}` + ); + + // Pattern from parser.ts that supports both single and double quotes + let regexpStr = "^[\\s\\\"']*(.*?)("; + regexpStr += versionedToolNames.join("|") + ")(\\s|[\\\"']\\s)(.*)$"; + + return new RegExp(regexpStr, "mg"); + } + + test("Test tool invocation with double quotes (original behavior)", () => { + const regex = buildToolInvocationRegex(["gcc", "cc", "clang"]); + const testCases = [ + { input: 'gcc -c main.c', shouldMatch: true }, + { input: '"gcc" -c main.c', shouldMatch: true }, + { input: '"/usr/bin/gcc" -c main.c', shouldMatch: true }, + ]; + + for (const testCase of testCases) { + regex.lastIndex = 0; + const match = regex.exec(testCase.input); + expect(match !== null).to.equal( + testCase.shouldMatch, + `Expected "${testCase.input}" to ${testCase.shouldMatch ? "match" : "not match"}` + ); + } + }); + + test("Test tool invocation with single quotes (mingw-make style)", () => { + const regex = buildToolInvocationRegex(["gcc", "cc", "clang"]); + const testCases = [ + { input: "'gcc' -c main.c", shouldMatch: true }, + { input: "'/usr/bin/gcc' -c main.c", shouldMatch: true }, + { input: "'clang' -c main.cpp", shouldMatch: true }, + ]; + + for (const testCase of testCases) { + regex.lastIndex = 0; + const match = regex.exec(testCase.input); + expect(match !== null).to.equal( + testCase.shouldMatch, + `Expected "${testCase.input}" to ${testCase.shouldMatch ? "match" : "not match"}` + ); + } + }); + + test("Test tool invocation without quotes", () => { + const regex = buildToolInvocationRegex(["gcc", "cc", "clang"]); + const testCases = [ + { input: "gcc -c main.c", shouldMatch: true }, + { input: "/usr/bin/gcc -c main.c", shouldMatch: true }, + { input: "clang -c main.cpp", shouldMatch: true }, + { input: "cc -c test.c", shouldMatch: true }, + ]; + + for (const testCase of testCases) { + regex.lastIndex = 0; + const match = regex.exec(testCase.input); + expect(match !== null).to.equal( + testCase.shouldMatch, + `Expected "${testCase.input}" to ${testCase.shouldMatch ? "match" : "not match"}` + ); + } + }); + + test("Test tool path extraction with various quote styles", () => { + const regex = buildToolInvocationRegex(["gcc"]); + const testCases = [ + { input: "/usr/bin/gcc -c main.c", expectedPath: "/usr/bin/" }, + { input: '"/usr/bin/gcc" -c main.c', expectedPath: "/usr/bin/" }, + { input: "'/usr/bin/gcc' -c main.c", expectedPath: "/usr/bin/" }, + { input: "gcc -c main.c", expectedPath: "" }, + { input: "'gcc' -c main.c", expectedPath: "" }, + { input: '"gcc" -c main.c', expectedPath: "" }, + ]; + + for (const testCase of testCases) { + regex.lastIndex = 0; + const match = regex.exec(testCase.input); + expect(match).to.not.be.null; + if (match) { + expect(match[1]).to.equal( + testCase.expectedPath, + `Expected path "${testCase.expectedPath}" for input "${testCase.input}", got "${match[1]}"` + ); + } + } + }); +}); + // TODO: refactor initialization and cleanup of each test suite("Fake dryrun parsing", () => { suiteSetup(async function (this: Mocha.Context) {