Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/metricsAnalyzer/metricsAnalyzerFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ export interface UnifiedFunctionMetrics {
complexity: number;
/** Array of individual complexity details that contribute to the total score */
details: UnifiedMetricsDetail[];
/** Line number where the function definition starts (1-based) */
/** Line number where the function definition starts (0-based) */
startLine: number;
/** Line number where the function definition ends (1-based) */
/** Line number where the function definition ends (0-based) */
endLine: number;
/** Column number where the function definition starts (0-based) */
startColumn: number;
Expand Down
20 changes: 14 additions & 6 deletions src/providers/codeLensProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,26 @@ export class MetricsCodeLensProvider implements vscode.CodeLensProvider {
const hasPathSeparators = normalizedPattern.includes("/");

if (hasPathSeparators) {
// Pattern contains path separators - match against full path
// Pattern contains path separators - match against full path.
// Step 1: preserve wildcards as null-byte placeholders so they survive
// regex escaping; Step 2: escape regex metacharacters in the
// literal portions; Step 3: restore wildcards as regex tokens.
const regexPattern = normalizedPattern
.replace(/\*\*/g, "___DOUBLESTAR___") // Temporary placeholder
.replace(/\*/g, "[^/]*") // Single * matches within directory
.replace(/___DOUBLESTAR___/g, ".*"); // ** matches across directories
.replace(/\*\*/g, "\x00DS\x00") // placeholder for **
.replace(/\*/g, "\x00S\x00") // placeholder for *
.replace(/[.+?^${}()|[\]\\]/g, "\\$&") // escape regex metacharacters
.replace(/\x00DS\x00/g, ".*") // ** matches across directories
.replace(/\x00S\x00/g, "[^/]*"); // single * matches within directory

const regex = new RegExp(`^${regexPattern}$`);
return regex.test(normalizedPath);
} else {
// Pattern has no path separators - match against filename only
// Pattern has no path separators - match against filename only.
const filename = normalizedPath.split("/").pop() || "";
const regexPattern = normalizedPattern.replace(/\*/g, ".*"); // * matches any characters in filename
const regexPattern = normalizedPattern
.replace(/\*/g, "\x00S\x00") // placeholder for *
.replace(/[.+?^${}()|[\]\\]/g, "\\$&") // escape regex metacharacters
.replace(/\x00S\x00/g, ".*"); // * matches any characters in filename

const regex = new RegExp(`^${regexPattern}$`);
return regex.test(filename);
Expand Down
38 changes: 38 additions & 0 deletions src/test/providers/codeLensProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,44 @@ suite("Metrics Code Lens Provider Tests", () => {
},
{ pattern: "test*", path: "/path/test.cs", shouldExclude: true },
{ pattern: "test*", path: "/path/src.cs", shouldExclude: false },
// Regex metacharacter escaping: literal dots must not act as wildcards
// Full-path branch (pattern contains a path separator)
{
pattern: "**/*.min.js",
path: "/path/to/foo.min.js",
shouldExclude: true,
},
{
pattern: "**/*.min.js",
path: "/path/to/fooXminXjs",
shouldExclude: false,
},
{
pattern: "**/*.spec.*",
path: "/path/to/app.spec.ts",
shouldExclude: true,
},
{
pattern: "**/*.spec.*",
path: "/path/to/appXspecXts",
shouldExclude: false,
},
// Filename-only branch (pattern has no path separator)
{
pattern: "*.generated.*",
path: "/path/fooXgeneratedXcs",
shouldExclude: false,
},
{
pattern: "*.min.js",
path: "/path/foo.min.js",
shouldExclude: true,
},
{
pattern: "*.min.js",
path: "/path/fooXminXjs",
shouldExclude: false,
},
];

for (const testCase of testCases) {
Expand Down
Loading