From 7a12a7be5efa7d449e1c6f680a287bb3146ba9d7 Mon Sep 17 00:00:00 2001 From: Colen Garoutte-Carson Date: Mon, 11 Nov 2024 18:38:16 -0800 Subject: [PATCH 1/7] Fix matching of editorConfig sections patterns --- Extension/src/LanguageServer/editorConfig.ts | 411 ++++++++++++++++++- Extension/test/unit/matchesSection.test.ts | 361 ++++++++++++++++ 2 files changed, 764 insertions(+), 8 deletions(-) create mode 100644 Extension/test/unit/matchesSection.test.ts diff --git a/Extension/src/LanguageServer/editorConfig.ts b/Extension/src/LanguageServer/editorConfig.ts index 21a73673c6..aee592eefb 100644 --- a/Extension/src/LanguageServer/editorConfig.ts +++ b/Extension/src/LanguageServer/editorConfig.ts @@ -61,13 +61,408 @@ export function mapWrapToEditorConfig(value: string | undefined): string { return "never"; } -function matchesSection(filePath: string, section: string): boolean { - const fileName: string = path.basename(filePath); - // Escape all regex special characters except '*' and '?'. - // Convert wildcards '*' to '.*' and '?' to '.'. - const sectionPattern = section.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*').replace(/\?/g, '.'); - const regex: RegExp = new RegExp(`^${sectionPattern}$`); - return regex.test(fileName); +let isValid: boolean = true; +let relativeToCurrentDir: boolean = false; + +// Helper function to find matching '}' for a given '{' position +function findMatchingBrace(pattern: string, start: number): number { + let braceLevel = 0; + let i = start; + while (i < pattern.length) { + const c = pattern[i]; + switch (c) { + case "\\": + if (i === pattern.length - 1) { + return -1; + } + i += 2; + break; + case "{": + braceLevel++; + i++; + break; + case "}": + braceLevel--; + if (braceLevel === 0) { + return i; + } + i++; + break; + default: + i++; + break; + } + } + return -1; +} + +// Function to handle brace expansion for ranges and lists +function handleBraceExpansion(pattern: string): string { + const rangeMatch = pattern.match(/^\s*(-?\d+)\s*\.\.\s*(-?\d+)\s*$/); + if (rangeMatch) { + const [, start, end] = rangeMatch.map(Number); + return buildRangeRegex(start, end); + } + + const options = []; + let braceLevel = 0; + let currentOption = ''; + let i = 0; + while (i < pattern.length) { + const c = pattern[i]; + switch (c) { + case "\\": + if (i === pattern.length - 1) { + isValid = false; + return ""; + } + currentOption += escapeRegex(pattern[i + 1]); + i += 2; + break; + case "{": + braceLevel++; + i++; + break; + case "}": + braceLevel--; + if (braceLevel === 0) { + options.push(convertSectionToRegExp(currentOption.trim())); + currentOption = ''; + } + i++; + break; + case ",": + if (braceLevel === 0) { + options.push(convertSectionToRegExp(currentOption.trim())); + currentOption = ''; + } else { + currentOption += c; + } + i++; + break; + default: + currentOption += c; + i++; + break; + } + } + + if (currentOption) { + options.push(convertSectionToRegExp(currentOption.trim())); + } + + return `(${options.join('|')})`; +} + +function buildRangeRegex(start: number, end: number): string { + if (start === end) { + return start.toString(); + } + if (start < 0 && end < 0) { + return `-${buildRangeRegex(-end, -start)}`; + } + if (start > end) { + isValid = false; + return ""; + } + if (start > 0) { + return buildPositiveRangeRegex(start, end); + } + // Pattern to match one or more zeros only if not followed by a non-zero digit + const zeroPattern = "(0+(?![1-9]))"; + if (end === 0) { + // If end is 0, start must be negative. + const pattern = buildZeroToNRegex(-start); + return `(${zeroPattern}|(-${pattern}))`; + } + // end is >0. + const endPattern = buildZeroToNRegex(end); + if (start === 0) { + return `(${zeroPattern}|${endPattern})`; + } + const startPattern = buildZeroToNRegex(-start); + return `(${zeroPattern}|(-${startPattern})|${endPattern})`; +} + +function buildZeroToNRegex(n: number): string { + const nStr = n.toString(); + const length = nStr.length; + const parts: string[] = []; + + // Pattern to remove leading zeros when followed by a non-zero digit + const leadingZerosPattern = "(0*(?=[1-9]))"; + let prefix = ""; + + if (length > 1) { + // Handle numbers with fewer digits than `n` + + // Single-digit numbers from 0 to 9 + parts.push(`[0-9]`); + for (let i = 2; i < length; i++) { + // Multi-digit numbers with fewer digits than `n` + parts.push(`([1-9]\\d{0,${i - 1}})`); + } + + // Build the main pattern by comparing each digit position + for (let i = 0; i < length - 1; i++) { + const digit = parseInt(nStr[i]); + if (digit > 1) { + parts.push(`(${prefix}[0-${digit - 1}]${"\\d".repeat(length - i - 1)})`); + } + prefix += digit; + } + } + const digit = parseInt(nStr[length - 1]); + if (digit === 0) { + parts.push(`(${prefix}0)`); + } else { + parts.push(`(${prefix}[0-${digit}])`); + } + + // Combine everything without start and end anchors + return `(${leadingZerosPattern}(${parts.join("|")}))`; +} + +// start will be >0, end will be >start. +function buildPositiveRangeRegex(start: number, end: number): string { + const startStr = start.toString(); + const endStr = end.toString(); + const startLength = startStr.length; + const endLength = endStr.length; + const parts: string[] = []; + + // Pattern to remove leading zeros when followed by a non-zero digit + const leadingZerosPattern = "(0*(?=[1-9]))"; + + if (startLength === endLength) { + if (startLength === 1) { + return `(${leadingZerosPattern}([${startStr}-${endStr}]))`; + } + + // First, any identical leading digits are added to the prefix. + let sharedPrefix = ""; + let i = 0; + while (i < startLength && startStr[i] === endStr[i]) { + sharedPrefix += startStr[i]; + i++; + } + if (i === startLength - 1) { + // Special case for only 1 digit lefts) + parts.push(`(${sharedPrefix}[${startStr[i]}-${endStr[i]}])`); + } else { + + // Now we break the remaining digits into three parts: + // Part 1. With the new start digit, check any of the remaining against ranges to 9. + let prefix = sharedPrefix + startStr[i]; + for (let i2 = i + 1; i2 < startLength - 1; i2++) { + const startDigit = parseInt(startStr[i2]); + if (startDigit === 8) { + parts.push(`(${prefix}9${"\\d".repeat(startLength - i2 - 1)})`); + } else if (startDigit !== 9) { + parts.push(`(${prefix}[${startDigit + 1}-9]${"\\d".repeat(startLength - i2 - 1)})`); + } + prefix += startStr[i2]; + } + const startDigit = parseInt(startStr[startLength - 1]); + if (startDigit === 9) { + parts.push(`(${prefix}9)`); + } else { + parts.push(`(${prefix}[${startDigit}-9])`); + } + + // Part 2. Any larger start digit less than the end digit, should match the full range for the remaining digits. + let curDigit = parseInt(startStr[i]) + 1; + const firstEndDigit = parseInt(endStr[i]); + while (curDigit < firstEndDigit) { + parts.push(`(${sharedPrefix}${curDigit}${"\\d".repeat(startLength - i - 1)})`); + curDigit++; + } + + // Part 3. With the new end digit, check for any the remaining against ranges from 0. + prefix = sharedPrefix + endStr[i]; + for (let i2 = i + 1; i2 < endLength - 1; i2++) { + const endDigit = parseInt(endStr[i2]); + if (endDigit === 1) { + parts.push(`(${prefix}0${"\\d".repeat(endLength - i2 - 1)})`); + } else if (endDigit !== 0) { + parts.push(`(${prefix}[0-${endDigit - 1}]${"\\d".repeat(endLength - i2 - 1)})`); + } + prefix += endStr[i2]; + } + const endDigit = parseInt(endStr[endLength - 1]); + if (endDigit === 0) { + parts.push(`(${prefix}0)`); + } else { + parts.push(`(${prefix}[0-${endDigit}])`); + } + } + } else { + // endLength > startLength + + // Add patterns for numbers with the same number of digits as `start` + let startPrefix = ""; + for (let i = 0; i < startLength - 1; i++) { + const startDigit = parseInt(startStr[i]); + if (startDigit === 8) { + parts.push(`(${startPrefix}9\\d{${startLength - i - 1}})`); + } + else if (startDigit !== 9) { + parts.push(`(${startPrefix}[${startDigit + 1}-9]\\d{${startLength - i - 1}})`); + } + // if startDigit === 9, we don't need to add a pattern for this digit + startPrefix += startStr[i]; + } + const startDigit = parseInt(startStr[startLength - 1]); + if (startDigit === 9) { + parts.push(`(${startPrefix}9)`); + } else { + parts.push(`(${startPrefix}[${startDigit}-9])`); + } + + // Handle numbers with more digits than 'start' and fewer digits than 'end' + for (let i = startLength + 1; i < endLength; i++) { + // Multi-digit numbers with more digits than 'start' and fewer digits than 'end' + parts.push(`([1-9]\\d{${i - 1}})`); + } + + // Add patterns for numbers with the same number of digits as `end` + let endPrefix = ""; + for (let i = 0; i < endLength - 1; i++) { + const endDigit = parseInt(endStr[i]); + if (endDigit === 1) { + if (i !== 0) { + parts.push(`(${endPrefix}0\\d{${endLength - i - 1}})`); + } + } else if (endDigit !== 0) { + parts.push(`(${endPrefix}[0-${endDigit - 1}]\\d{${endLength - i - 1}})`); + } + // endDigit === 0, we don't need to add a pattern for this digit + endPrefix += endStr[i]; + } + const endDigit = parseInt(endStr[endLength - 1]); + if (endDigit === 0) { + parts.push(`(${endPrefix}0)`); + } else { + parts.push(`(${endPrefix}[0-${endDigit}])`); + } + } + + // Combine everything without start and end anchors + return `(${leadingZerosPattern}(${parts.join("|")}))`; +} + +// Utility to escape regex special characters in a string +function escapeRegex(str: string): string { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +function convertSectionToRegExp(pattern: string): string { + let regExp = ''; + let i = 0; + while (i < pattern.length) { + const c = pattern[i]; + switch (c) { + case '*': + if (i < pattern.length - 1 && pattern[i + 1] === '*') { + if (i > 0 && pattern[i - 1] !== '/') { + isValid = false; + return ""; + } + i++; + if (i < pattern.length - 1) { + if (pattern[i + 1] !== '/') { + isValid = false; + return ""; + } + i++; + regExp += '(?:(.*\\/)?|\\/)?'; + } + else { + regExp += '.*'; + } + } else { + regExp += '[^\\/]*'; + } + i++; + break; + case '?': + regExp += '.'; + i += 1; + break; + case '[': + const endBracket = pattern.indexOf(']', i); + if (endBracket === -1) { + isValid = false; + return ""; + } + const charClass = pattern.slice(i + 1, endBracket); + if (charClass.startsWith('!')) { + regExp += `[^${escapeRegex(charClass.slice(1))}]`; + } else { + regExp += `[${escapeRegex(charClass)}]`; + } + i = endBracket + 1; + break; + case '{': + const endBrace = findMatchingBrace(pattern, i); + if (endBrace === -1) { + isValid = false; + return ""; + } + const braceContent = pattern.slice(i + 1, endBrace); + regExp += handleBraceExpansion(braceContent); + if (!isValid) { + return ""; + } + i = endBrace + 1; + break; + case "/": + if (i === pattern.length - 1) { + isValid = false; + return ""; + } + relativeToCurrentDir = true; + regExp += '\\/'; + i++; + break; + case '\\': + if (i === pattern.length - 1) { + isValid = false; + return ""; + } + regExp += escapeRegex(pattern[i + 1]); + i += 2; + break; + default: + regExp += escapeRegex(c); + i++; + break; + } + } + return regExp; +} + +export function matchesSection(currentDir: string, filePath: string, section: string): boolean { + isValid = true; + relativeToCurrentDir = false; + const regExpString: string = `^${convertSectionToRegExp(section)}$`; + if (!isValid) { + return false; + } + const regexp: RegExp = new RegExp(regExpString); + let compareWith: string; + if (relativeToCurrentDir) { + if (!filePath.startsWith(currentDir)) { + return false; + } + compareWith = filePath.slice(currentDir.length); + if (compareWith.startsWith('/')) { + compareWith = compareWith.slice(1); + } + } else { + compareWith = path.basename(filePath); + } + return regexp.test(compareWith); } function parseEditorConfigContent(content: string): Record { @@ -140,7 +535,7 @@ function getEditorConfig(filePath: string): any { // Match sections and combine configurations. Object.keys(configData).forEach((section: string) => { - if (section !== '*' && matchesSection(filePath, section)) { + if (section !== '*' && matchesSection(currentDir, filePath, section)) { combinedConfig = { ...combinedConfig, ...configData[section] diff --git a/Extension/test/unit/matchesSection.test.ts b/Extension/test/unit/matchesSection.test.ts new file mode 100644 index 0000000000..9985b8498a --- /dev/null +++ b/Extension/test/unit/matchesSection.test.ts @@ -0,0 +1,361 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. + * See 'LICENSE' in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import { ok } from 'assert'; +import { describe } from 'mocha'; +import { matchesSection } from '../../src/LanguageServer/editorConfig'; + +describe('Test editorConfig section pattern matching', () => { + const editorConfigPath = "/project"; + + it('matchesSection test: *', () => { + const pattern = "*"; + const filePath = "/project/subdir/file.cpp"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + }); + + it('matchesSection test: *.cpp', () => { + const pattern = "*.cpp"; + const filePath = "/project/subdir/file.cpp"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + }); + + it('matchesSection test: subdir/*.c', () => { + const pattern: string = "subdir/*.c"; + + let filePath: string = "/project/subdir/file.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/file.cpp"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/file.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/other/subdir/file.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + }); + + it('matchesSection test: ????.cpp', () => { + const pattern = "????.cpp"; + + let filePath: string = "/project/subdir/file.cpp"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/file2.cpp"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/x.cpp"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + }); + + it('matchesSection test: [abc].c', () => { + const pattern = "[abc].c"; + + let filePath: string = "/project/subdir/a.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/z.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + }); + + it('matchesSection test: [!abc].c', () => { + const pattern = "[!abc].c"; + + let filePath: string = "/project/subdir/a.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/z.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + }); + + it('matchesSection test: test.{c, h, cpp}', () => { + const pattern = "test.{c, h, cpp}"; + + let filePath: string = "/project/subdir/test.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test.h"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test.cpp"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test.hpp"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + }); + + it('matchesSection test: test{1..3}.c', () => { + const pattern = "test{1..3}.c"; + + let filePath: string = "/project/subdir/test1.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test2.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test3.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test4.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test0.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test01.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test00.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + }); + + it('matchesSection test: test{0..100}.c', () => { + const pattern = "test{0..100}.c"; + + let filePath: string = "/project/subdir/test0.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test100.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test5.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test50.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test00.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test050.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test101.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test-1.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test1000.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test500.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + }); + + it('matchesSection test: test{10..1000}.c', () => { + const pattern = "test{10..1000}.c"; + + let filePath: string = "/project/subdir/test10.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test100.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test100.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test1001.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + }); + + it('matchesSection test: test{0..101}.c', () => { + const pattern = "test{0..101}.c"; + + let filePath: string = "/project/subdir/test0.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test10.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test100.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test100.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test101.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test102.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + }); + + it('matchesSection test: test{0..456}.c', () => { + const pattern = "test{0..456}.c"; + + let filePath: string = "/project/subdir/test0.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test400.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test450.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test456.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test457.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test460.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test500.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + }); + + it('matchesSection test: test{123..456}.c', () => { + const pattern = "test{123..456}.c"; + + let filePath: string = "/project/subdir/test123.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test299.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test456.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test12.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test122.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test457.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test-123.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + }); + + it('matchesSection test: test{123..456}0.c', () => { + const pattern = "test{123..456}0.c"; + + let filePath: string = "/project/subdir/test1230.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test2990.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test4560.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test12.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test120.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test1220.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test4570.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test-1230.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test123.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + }); + + it('matchesSection test: test{123..45678}.c', () => { + const pattern = "test{123..45678}.c"; + + let filePath: string = "/project/subdir/test123.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test999.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test9999.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test45678.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/test12.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test45679.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test123x.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/subdir/test-9999.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + }); + + it('matchesSection test: *.{c, h, cpp}', () => { + const pattern = "*.{c, h, cpp}"; + + let filePath: string = "/project/subdir/a.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/other/subdir/b.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/c.h"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/subdir/d.cpp"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/a.c/other"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + }); + + it('matchesSection test: src/{test, lib}/**', () => { + const pattern = "src/{test, lib}/**"; + + let filePath: string = "/project/src/test/subdir/test.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/src/test/test.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/src/lib/test.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/src/other/test.cpp"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/other/src/test/test.c"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + }); + + it('matchesSection test: src/{test, lib}/**/*.{c, h, cpp}', () => { + const pattern = "src/{test, lib}/**/*.{c, h, cpp}"; + + let filePath: string = "/project/src/test/subdir/test.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/src/test/test.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/src/lib/test.c"; + ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); + + filePath = "/project/src/other/test.cpp"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/other/src/test/test.hpp"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/src/test/subdir/test.hpp"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/src/test/test.hpp"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + + filePath = "/project/src/lib/test.hpp"; + ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); + }); +}); From 9b369de8aac2969e2fe7dfe204bdeec8c4cd3a3d Mon Sep 17 00:00:00 2001 From: Colen Garoutte-Carson Date: Tue, 12 Nov 2024 14:35:27 -0800 Subject: [PATCH 2/7] Address PR feedback. Fix slash direction on Windows. --- Extension/src/LanguageServer/editorConfig.ts | 59 +++++++++++--------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/Extension/src/LanguageServer/editorConfig.ts b/Extension/src/LanguageServer/editorConfig.ts index aee592eefb..3a7bd57266 100644 --- a/Extension/src/LanguageServer/editorConfig.ts +++ b/Extension/src/LanguageServer/editorConfig.ts @@ -6,6 +6,7 @@ import * as fs from 'fs'; import * as path from 'path'; +import { isWindows } from '../constants'; export const cachedEditorConfigSettings: Map = new Map(); @@ -64,39 +65,38 @@ export function mapWrapToEditorConfig(value: string | undefined): string { let isValid: boolean = true; let relativeToCurrentDir: boolean = false; -// Helper function to find matching '}' for a given '{' position +// Helper function to find matching '}' for a given '{' position. function findMatchingBrace(pattern: string, start: number): number { let braceLevel = 0; - let i = start; - while (i < pattern.length) { + for (let i = start; i < pattern.length; i++) { const c = pattern[i]; switch (c) { case "\\": if (i === pattern.length - 1) { return -1; } - i += 2; + i++; break; case "{": braceLevel++; - i++; break; case "}": braceLevel--; if (braceLevel === 0) { return i; } - i++; + if (braceLevel < 0) { + return -1; + } break; default: - i++; break; } } return -1; } -// Function to handle brace expansion for ranges and lists +// Function to handle brace expansion for ranges and lists. function handleBraceExpansion(pattern: string): string { const rangeMatch = pattern.match(/^\s*(-?\d+)\s*\.\.\s*(-?\d+)\s*$/); if (rangeMatch) { @@ -168,7 +168,7 @@ function buildRangeRegex(start: number, end: number): string { if (start > 0) { return buildPositiveRangeRegex(start, end); } - // Pattern to match one or more zeros only if not followed by a non-zero digit + // Pattern to match one or more zeros only if not followed by a non-zero digit. const zeroPattern = "(0+(?![1-9]))"; if (end === 0) { // If end is 0, start must be negative. @@ -189,21 +189,21 @@ function buildZeroToNRegex(n: number): string { const length = nStr.length; const parts: string[] = []; - // Pattern to remove leading zeros when followed by a non-zero digit + // Pattern to remove leading zeros when followed by a non-zero digit. const leadingZerosPattern = "(0*(?=[1-9]))"; let prefix = ""; if (length > 1) { - // Handle numbers with fewer digits than `n` + // Handle numbers with fewer digits than `n`. // Single-digit numbers from 0 to 9 parts.push(`[0-9]`); for (let i = 2; i < length; i++) { - // Multi-digit numbers with fewer digits than `n` + // Multi-digit numbers with fewer digits than `n`. parts.push(`([1-9]\\d{0,${i - 1}})`); } - // Build the main pattern by comparing each digit position + // Build the main pattern by comparing each digit position. for (let i = 0; i < length - 1; i++) { const digit = parseInt(nStr[i]); if (digit > 1) { @@ -219,7 +219,7 @@ function buildZeroToNRegex(n: number): string { parts.push(`(${prefix}[0-${digit}])`); } - // Combine everything without start and end anchors + // Combine everything without start and end anchors. return `(${leadingZerosPattern}(${parts.join("|")}))`; } @@ -231,7 +231,7 @@ function buildPositiveRangeRegex(start: number, end: number): string { const endLength = endStr.length; const parts: string[] = []; - // Pattern to remove leading zeros when followed by a non-zero digit + // Pattern to remove leading zeros when followed by a non-zero digit. const leadingZerosPattern = "(0*(?=[1-9]))"; if (startLength === endLength) { @@ -297,9 +297,7 @@ function buildPositiveRangeRegex(start: number, end: number): string { } } } else { - // endLength > startLength - - // Add patterns for numbers with the same number of digits as `start` + // Add patterns for numbers with the same number of digits as `start`. let startPrefix = ""; for (let i = 0; i < startLength - 1; i++) { const startDigit = parseInt(startStr[i]); @@ -309,7 +307,7 @@ function buildPositiveRangeRegex(start: number, end: number): string { else if (startDigit !== 9) { parts.push(`(${startPrefix}[${startDigit + 1}-9]\\d{${startLength - i - 1}})`); } - // if startDigit === 9, we don't need to add a pattern for this digit + // if startDigit === 9, we don't need to add a pattern for this digit. startPrefix += startStr[i]; } const startDigit = parseInt(startStr[startLength - 1]); @@ -319,13 +317,13 @@ function buildPositiveRangeRegex(start: number, end: number): string { parts.push(`(${startPrefix}[${startDigit}-9])`); } - // Handle numbers with more digits than 'start' and fewer digits than 'end' + // Handle numbers with more digits than 'start' and fewer digits than 'end'. for (let i = startLength + 1; i < endLength; i++) { - // Multi-digit numbers with more digits than 'start' and fewer digits than 'end' + // Multi-digit numbers with more digits than 'start' and fewer digits than 'end'. parts.push(`([1-9]\\d{${i - 1}})`); } - // Add patterns for numbers with the same number of digits as `end` + // Add patterns for numbers with the same number of digits as `end`. let endPrefix = ""; for (let i = 0; i < endLength - 1; i++) { const endDigit = parseInt(endStr[i]); @@ -336,7 +334,7 @@ function buildPositiveRangeRegex(start: number, end: number): string { } else if (endDigit !== 0) { parts.push(`(${endPrefix}[0-${endDigit - 1}]\\d{${endLength - i - 1}})`); } - // endDigit === 0, we don't need to add a pattern for this digit + // endDigit === 0, we don't need to add a pattern for this digit. endPrefix += endStr[i]; } const endDigit = parseInt(endStr[endLength - 1]); @@ -347,11 +345,11 @@ function buildPositiveRangeRegex(start: number, end: number): string { } } - // Combine everything without start and end anchors + // Combine everything without start and end anchors. return `(${leadingZerosPattern}(${parts.join("|")}))`; } -// Utility to escape regex special characters in a string +// Utility to escape regex special characters in a string. function escapeRegex(str: string): string { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } @@ -518,6 +516,10 @@ function getEditorConfig(filePath: string): any { let currentDir: string = path.dirname(filePath); const rootDir: string = path.parse(currentDir).root; + if (isWindows) { + filePath = filePath.replace(/\\/g, '/'); + } + // Traverse from the file's directory to the root directory. for (; ;) { const editorConfigPath: string = path.join(currentDir, '.editorconfig'); @@ -533,9 +535,14 @@ function getEditorConfig(filePath: string): any { }; } + let currentDirForwardSlashes: string = currentDir; + if (isWindows) { + currentDirForwardSlashes = currentDir.replace(/\\/g, '/'); + } + // Match sections and combine configurations. Object.keys(configData).forEach((section: string) => { - if (section !== '*' && matchesSection(currentDir, filePath, section)) { + if (section !== '*' && matchesSection(currentDirForwardSlashes, filePath, section)) { combinedConfig = { ...combinedConfig, ...configData[section] From 9bf7f09d261e1e29b6657d68104c867b803874d0 Mon Sep 17 00:00:00 2001 From: Colen Garoutte-Carson Date: Tue, 12 Nov 2024 16:02:55 -0800 Subject: [PATCH 3/7] Switch to using minimatch and code copied from editorconfig-core-js --- Extension/src/LanguageServer/editorConfig.ts | 423 +------------------ Extension/test/unit/matchesSection.test.ts | 361 ---------------- 2 files changed, 22 insertions(+), 762 deletions(-) delete mode 100644 Extension/test/unit/matchesSection.test.ts diff --git a/Extension/src/LanguageServer/editorConfig.ts b/Extension/src/LanguageServer/editorConfig.ts index 3a7bd57266..d10b4611ad 100644 --- a/Extension/src/LanguageServer/editorConfig.ts +++ b/Extension/src/LanguageServer/editorConfig.ts @@ -5,6 +5,7 @@ 'use strict'; import * as fs from 'fs'; +import { Minimatch } from 'minimatch'; import * as path from 'path'; import { isWindows } from '../constants'; @@ -62,405 +63,25 @@ export function mapWrapToEditorConfig(value: string | undefined): string { return "never"; } -let isValid: boolean = true; -let relativeToCurrentDir: boolean = false; - -// Helper function to find matching '}' for a given '{' position. -function findMatchingBrace(pattern: string, start: number): number { - let braceLevel = 0; - for (let i = start; i < pattern.length; i++) { - const c = pattern[i]; - switch (c) { - case "\\": - if (i === pattern.length - 1) { - return -1; - } - i++; - break; - case "{": - braceLevel++; - break; - case "}": - braceLevel--; - if (braceLevel === 0) { - return i; - } - if (braceLevel < 0) { - return -1; - } - break; - default: - break; - } - } - return -1; -} - -// Function to handle brace expansion for ranges and lists. -function handleBraceExpansion(pattern: string): string { - const rangeMatch = pattern.match(/^\s*(-?\d+)\s*\.\.\s*(-?\d+)\s*$/); - if (rangeMatch) { - const [, start, end] = rangeMatch.map(Number); - return buildRangeRegex(start, end); - } - - const options = []; - let braceLevel = 0; - let currentOption = ''; - let i = 0; - while (i < pattern.length) { - const c = pattern[i]; - switch (c) { - case "\\": - if (i === pattern.length - 1) { - isValid = false; - return ""; - } - currentOption += escapeRegex(pattern[i + 1]); - i += 2; - break; - case "{": - braceLevel++; - i++; - break; - case "}": - braceLevel--; - if (braceLevel === 0) { - options.push(convertSectionToRegExp(currentOption.trim())); - currentOption = ''; - } - i++; - break; - case ",": - if (braceLevel === 0) { - options.push(convertSectionToRegExp(currentOption.trim())); - currentOption = ''; - } else { - currentOption += c; - } - i++; - break; - default: - currentOption += c; - i++; - break; - } - } - - if (currentOption) { - options.push(convertSectionToRegExp(currentOption.trim())); - } - - return `(${options.join('|')})`; -} - -function buildRangeRegex(start: number, end: number): string { - if (start === end) { - return start.toString(); - } - if (start < 0 && end < 0) { - return `-${buildRangeRegex(-end, -start)}`; - } - if (start > end) { - isValid = false; - return ""; - } - if (start > 0) { - return buildPositiveRangeRegex(start, end); - } - // Pattern to match one or more zeros only if not followed by a non-zero digit. - const zeroPattern = "(0+(?![1-9]))"; - if (end === 0) { - // If end is 0, start must be negative. - const pattern = buildZeroToNRegex(-start); - return `(${zeroPattern}|(-${pattern}))`; - } - // end is >0. - const endPattern = buildZeroToNRegex(end); - if (start === 0) { - return `(${zeroPattern}|${endPattern})`; - } - const startPattern = buildZeroToNRegex(-start); - return `(${zeroPattern}|(-${startPattern})|${endPattern})`; -} - -function buildZeroToNRegex(n: number): string { - const nStr = n.toString(); - const length = nStr.length; - const parts: string[] = []; - - // Pattern to remove leading zeros when followed by a non-zero digit. - const leadingZerosPattern = "(0*(?=[1-9]))"; - let prefix = ""; - - if (length > 1) { - // Handle numbers with fewer digits than `n`. - - // Single-digit numbers from 0 to 9 - parts.push(`[0-9]`); - for (let i = 2; i < length; i++) { - // Multi-digit numbers with fewer digits than `n`. - parts.push(`([1-9]\\d{0,${i - 1}})`); - } - - // Build the main pattern by comparing each digit position. - for (let i = 0; i < length - 1; i++) { - const digit = parseInt(nStr[i]); - if (digit > 1) { - parts.push(`(${prefix}[0-${digit - 1}]${"\\d".repeat(length - i - 1)})`); - } - prefix += digit; - } - } - const digit = parseInt(nStr[length - 1]); - if (digit === 0) { - parts.push(`(${prefix}0)`); - } else { - parts.push(`(${prefix}[0-${digit}])`); - } - - // Combine everything without start and end anchors. - return `(${leadingZerosPattern}(${parts.join("|")}))`; -} - -// start will be >0, end will be >start. -function buildPositiveRangeRegex(start: number, end: number): string { - const startStr = start.toString(); - const endStr = end.toString(); - const startLength = startStr.length; - const endLength = endStr.length; - const parts: string[] = []; - - // Pattern to remove leading zeros when followed by a non-zero digit. - const leadingZerosPattern = "(0*(?=[1-9]))"; - - if (startLength === endLength) { - if (startLength === 1) { - return `(${leadingZerosPattern}([${startStr}-${endStr}]))`; - } - - // First, any identical leading digits are added to the prefix. - let sharedPrefix = ""; - let i = 0; - while (i < startLength && startStr[i] === endStr[i]) { - sharedPrefix += startStr[i]; - i++; - } - if (i === startLength - 1) { - // Special case for only 1 digit lefts) - parts.push(`(${sharedPrefix}[${startStr[i]}-${endStr[i]}])`); - } else { - - // Now we break the remaining digits into three parts: - // Part 1. With the new start digit, check any of the remaining against ranges to 9. - let prefix = sharedPrefix + startStr[i]; - for (let i2 = i + 1; i2 < startLength - 1; i2++) { - const startDigit = parseInt(startStr[i2]); - if (startDigit === 8) { - parts.push(`(${prefix}9${"\\d".repeat(startLength - i2 - 1)})`); - } else if (startDigit !== 9) { - parts.push(`(${prefix}[${startDigit + 1}-9]${"\\d".repeat(startLength - i2 - 1)})`); - } - prefix += startStr[i2]; - } - const startDigit = parseInt(startStr[startLength - 1]); - if (startDigit === 9) { - parts.push(`(${prefix}9)`); - } else { - parts.push(`(${prefix}[${startDigit}-9])`); - } - - // Part 2. Any larger start digit less than the end digit, should match the full range for the remaining digits. - let curDigit = parseInt(startStr[i]) + 1; - const firstEndDigit = parseInt(endStr[i]); - while (curDigit < firstEndDigit) { - parts.push(`(${sharedPrefix}${curDigit}${"\\d".repeat(startLength - i - 1)})`); - curDigit++; - } - - // Part 3. With the new end digit, check for any the remaining against ranges from 0. - prefix = sharedPrefix + endStr[i]; - for (let i2 = i + 1; i2 < endLength - 1; i2++) { - const endDigit = parseInt(endStr[i2]); - if (endDigit === 1) { - parts.push(`(${prefix}0${"\\d".repeat(endLength - i2 - 1)})`); - } else if (endDigit !== 0) { - parts.push(`(${prefix}[0-${endDigit - 1}]${"\\d".repeat(endLength - i2 - 1)})`); - } - prefix += endStr[i2]; - } - const endDigit = parseInt(endStr[endLength - 1]); - if (endDigit === 0) { - parts.push(`(${prefix}0)`); - } else { - parts.push(`(${prefix}[0-${endDigit}])`); - } - } - } else { - // Add patterns for numbers with the same number of digits as `start`. - let startPrefix = ""; - for (let i = 0; i < startLength - 1; i++) { - const startDigit = parseInt(startStr[i]); - if (startDigit === 8) { - parts.push(`(${startPrefix}9\\d{${startLength - i - 1}})`); - } - else if (startDigit !== 9) { - parts.push(`(${startPrefix}[${startDigit + 1}-9]\\d{${startLength - i - 1}})`); - } - // if startDigit === 9, we don't need to add a pattern for this digit. - startPrefix += startStr[i]; - } - const startDigit = parseInt(startStr[startLength - 1]); - if (startDigit === 9) { - parts.push(`(${startPrefix}9)`); - } else { - parts.push(`(${startPrefix}[${startDigit}-9])`); - } - - // Handle numbers with more digits than 'start' and fewer digits than 'end'. - for (let i = startLength + 1; i < endLength; i++) { - // Multi-digit numbers with more digits than 'start' and fewer digits than 'end'. - parts.push(`([1-9]\\d{${i - 1}})`); - } - - // Add patterns for numbers with the same number of digits as `end`. - let endPrefix = ""; - for (let i = 0; i < endLength - 1; i++) { - const endDigit = parseInt(endStr[i]); - if (endDigit === 1) { - if (i !== 0) { - parts.push(`(${endPrefix}0\\d{${endLength - i - 1}})`); - } - } else if (endDigit !== 0) { - parts.push(`(${endPrefix}[0-${endDigit - 1}]\\d{${endLength - i - 1}})`); - } - // endDigit === 0, we don't need to add a pattern for this digit. - endPrefix += endStr[i]; - } - const endDigit = parseInt(endStr[endLength - 1]); - if (endDigit === 0) { - parts.push(`(${endPrefix}0)`); - } else { - parts.push(`(${endPrefix}[0-${endDigit}])`); - } - } - - // Combine everything without start and end anchors. - return `(${leadingZerosPattern}(${parts.join("|")}))`; -} - -// Utility to escape regex special characters in a string. -function escapeRegex(str: string): string { - return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); -} - -function convertSectionToRegExp(pattern: string): string { - let regExp = ''; - let i = 0; - while (i < pattern.length) { - const c = pattern[i]; - switch (c) { - case '*': - if (i < pattern.length - 1 && pattern[i + 1] === '*') { - if (i > 0 && pattern[i - 1] !== '/') { - isValid = false; - return ""; - } - i++; - if (i < pattern.length - 1) { - if (pattern[i + 1] !== '/') { - isValid = false; - return ""; - } - i++; - regExp += '(?:(.*\\/)?|\\/)?'; - } - else { - regExp += '.*'; - } - } else { - regExp += '[^\\/]*'; - } - i++; - break; - case '?': - regExp += '.'; - i += 1; - break; - case '[': - const endBracket = pattern.indexOf(']', i); - if (endBracket === -1) { - isValid = false; - return ""; - } - const charClass = pattern.slice(i + 1, endBracket); - if (charClass.startsWith('!')) { - regExp += `[^${escapeRegex(charClass.slice(1))}]`; - } else { - regExp += `[${escapeRegex(charClass)}]`; - } - i = endBracket + 1; - break; - case '{': - const endBrace = findMatchingBrace(pattern, i); - if (endBrace === -1) { - isValid = false; - return ""; - } - const braceContent = pattern.slice(i + 1, endBrace); - regExp += handleBraceExpansion(braceContent); - if (!isValid) { - return ""; - } - i = endBrace + 1; - break; - case "/": - if (i === pattern.length - 1) { - isValid = false; - return ""; - } - relativeToCurrentDir = true; - regExp += '\\/'; - i++; - break; - case '\\': - if (i === pattern.length - 1) { - isValid = false; - return ""; - } - regExp += escapeRegex(pattern[i + 1]); - i += 2; - break; - default: - regExp += escapeRegex(c); - i++; - break; - } - } - return regExp; -} - -export function matchesSection(currentDir: string, filePath: string, section: string): boolean { - isValid = true; - relativeToCurrentDir = false; - const regExpString: string = `^${convertSectionToRegExp(section)}$`; - if (!isValid) { - return false; - } - const regexp: RegExp = new RegExp(regExpString); - let compareWith: string; - if (relativeToCurrentDir) { - if (!filePath.startsWith(currentDir)) { - return false; - } - compareWith = filePath.slice(currentDir.length); - if (compareWith.startsWith('/')) { - compareWith = compareWith.slice(1); - } - } else { - compareWith = path.basename(filePath); - } - return regexp.test(compareWith); +export function matchesSection(pathPrefix: string, filePath: string, section: string): boolean { + // The following code is copied from; https://github.com/editorconfig/editorconfig-core-js + const matchOptions = { matchBase: true, dot: true }; + pathPrefix = pathPrefix.replace(/[?*+@!()|[\]{}]/g, '\\$&'); + pathPrefix = pathPrefix.replace(/^#/, '\\#'); + switch (section.indexOf('/')) { + case -1: + section = `**/${section}`; + break; + case 0: + section = section.substring(1); + break; + default: + break; + } + section = section.replace(/\\\\/g, '\\\\\\\\'); + section = section.replace(/\*\*/g, '{*,**/**/**}'); + const matcher = new Minimatch(`${pathPrefix}/${section}`, matchOptions); + return matcher.match(filePath); } function parseEditorConfigContent(content: string): Record { @@ -488,9 +109,9 @@ function parseEditorConfigContent(content: string): Record { let value: any = values.join('=').trim(); // Convert boolean-like and numeric values. - if (value.toLowerCase() === 'true') { + if (value === 'true') { value = true; - } else if (value.toLowerCase() === 'false') { + } else if (value === 'false') { value = false; } else if (!isNaN(Number(value))) { value = Number(value); diff --git a/Extension/test/unit/matchesSection.test.ts b/Extension/test/unit/matchesSection.test.ts deleted file mode 100644 index 9985b8498a..0000000000 --- a/Extension/test/unit/matchesSection.test.ts +++ /dev/null @@ -1,361 +0,0 @@ -/* -------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All Rights Reserved. - * See 'LICENSE' in the project root for license information. - * ------------------------------------------------------------------------------------------ */ - -import { ok } from 'assert'; -import { describe } from 'mocha'; -import { matchesSection } from '../../src/LanguageServer/editorConfig'; - -describe('Test editorConfig section pattern matching', () => { - const editorConfigPath = "/project"; - - it('matchesSection test: *', () => { - const pattern = "*"; - const filePath = "/project/subdir/file.cpp"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - }); - - it('matchesSection test: *.cpp', () => { - const pattern = "*.cpp"; - const filePath = "/project/subdir/file.cpp"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - }); - - it('matchesSection test: subdir/*.c', () => { - const pattern: string = "subdir/*.c"; - - let filePath: string = "/project/subdir/file.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/file.cpp"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/file.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/other/subdir/file.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - }); - - it('matchesSection test: ????.cpp', () => { - const pattern = "????.cpp"; - - let filePath: string = "/project/subdir/file.cpp"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/file2.cpp"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/x.cpp"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - }); - - it('matchesSection test: [abc].c', () => { - const pattern = "[abc].c"; - - let filePath: string = "/project/subdir/a.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/z.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - }); - - it('matchesSection test: [!abc].c', () => { - const pattern = "[!abc].c"; - - let filePath: string = "/project/subdir/a.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/z.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - }); - - it('matchesSection test: test.{c, h, cpp}', () => { - const pattern = "test.{c, h, cpp}"; - - let filePath: string = "/project/subdir/test.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test.h"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test.cpp"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test.hpp"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - }); - - it('matchesSection test: test{1..3}.c', () => { - const pattern = "test{1..3}.c"; - - let filePath: string = "/project/subdir/test1.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test2.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test3.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test4.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test0.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test01.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test00.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - }); - - it('matchesSection test: test{0..100}.c', () => { - const pattern = "test{0..100}.c"; - - let filePath: string = "/project/subdir/test0.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test100.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test5.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test50.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test00.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test050.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test101.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test-1.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test1000.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test500.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - }); - - it('matchesSection test: test{10..1000}.c', () => { - const pattern = "test{10..1000}.c"; - - let filePath: string = "/project/subdir/test10.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test100.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test100.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test1001.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - }); - - it('matchesSection test: test{0..101}.c', () => { - const pattern = "test{0..101}.c"; - - let filePath: string = "/project/subdir/test0.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test10.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test100.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test100.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test101.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test102.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - }); - - it('matchesSection test: test{0..456}.c', () => { - const pattern = "test{0..456}.c"; - - let filePath: string = "/project/subdir/test0.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test400.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test450.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test456.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test457.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test460.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test500.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - }); - - it('matchesSection test: test{123..456}.c', () => { - const pattern = "test{123..456}.c"; - - let filePath: string = "/project/subdir/test123.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test299.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test456.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test12.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test122.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test457.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test-123.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - }); - - it('matchesSection test: test{123..456}0.c', () => { - const pattern = "test{123..456}0.c"; - - let filePath: string = "/project/subdir/test1230.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test2990.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test4560.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test12.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test120.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test1220.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test4570.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test-1230.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test123.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - }); - - it('matchesSection test: test{123..45678}.c', () => { - const pattern = "test{123..45678}.c"; - - let filePath: string = "/project/subdir/test123.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test999.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test9999.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test45678.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/test12.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test45679.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test123x.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/subdir/test-9999.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - }); - - it('matchesSection test: *.{c, h, cpp}', () => { - const pattern = "*.{c, h, cpp}"; - - let filePath: string = "/project/subdir/a.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/other/subdir/b.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/c.h"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/subdir/d.cpp"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/a.c/other"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - }); - - it('matchesSection test: src/{test, lib}/**', () => { - const pattern = "src/{test, lib}/**"; - - let filePath: string = "/project/src/test/subdir/test.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/src/test/test.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/src/lib/test.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/src/other/test.cpp"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/other/src/test/test.c"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - }); - - it('matchesSection test: src/{test, lib}/**/*.{c, h, cpp}', () => { - const pattern = "src/{test, lib}/**/*.{c, h, cpp}"; - - let filePath: string = "/project/src/test/subdir/test.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/src/test/test.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/src/lib/test.c"; - ok(matchesSection(editorConfigPath, filePath, pattern), `${pattern} should match: ${filePath}`); - - filePath = "/project/src/other/test.cpp"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/other/src/test/test.hpp"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/src/test/subdir/test.hpp"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/src/test/test.hpp"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - - filePath = "/project/src/lib/test.hpp"; - ok(!matchesSection(editorConfigPath, filePath, pattern), `${pattern} should not match: ${filePath}`); - }); -}); From 2abaa0f8eaf43de14ad9c49b68b4b97d8cae18b4 Mon Sep 17 00:00:00 2001 From: Colen Garoutte-Carson Date: Tue, 12 Nov 2024 16:04:39 -0800 Subject: [PATCH 4/7] Fix typo --- Extension/src/LanguageServer/editorConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extension/src/LanguageServer/editorConfig.ts b/Extension/src/LanguageServer/editorConfig.ts index d10b4611ad..b5c8aa1db0 100644 --- a/Extension/src/LanguageServer/editorConfig.ts +++ b/Extension/src/LanguageServer/editorConfig.ts @@ -64,7 +64,7 @@ export function mapWrapToEditorConfig(value: string | undefined): string { } export function matchesSection(pathPrefix: string, filePath: string, section: string): boolean { - // The following code is copied from; https://github.com/editorconfig/editorconfig-core-js + // The following code is copied from: https://github.com/editorconfig/editorconfig-core-js const matchOptions = { matchBase: true, dot: true }; pathPrefix = pathPrefix.replace(/[?*+@!()|[\]{}]/g, '\\$&'); pathPrefix = pathPrefix.replace(/^#/, '\\#'); From ca3725df6f6007da0a0461df0256a0f7430a7d32 Mon Sep 17 00:00:00 2001 From: Colen Garoutte-Carson Date: Tue, 12 Nov 2024 16:19:05 -0800 Subject: [PATCH 5/7] Upgrade minimatch version --- Extension/package.json | 4 ++-- Extension/src/LanguageServer/client.ts | 3 ++- Extension/yarn.lock | 11 +++++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Extension/package.json b/Extension/package.json index 733e2f4298..e66eefe776 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -6517,7 +6517,7 @@ "devDependencies": { "@octokit/rest": "^20.1.1", "@types/glob": "^7.2.0", - "@types/minimatch": "^3.0.5", + "@types/minimatch": "^5.1.2", "@types/mocha": "^10.0.6", "@types/node": "^20.14.2", "@types/node-fetch": "^2.6.11", @@ -6569,7 +6569,7 @@ "comment-json": "^4.2.3", "escape-string-regexp": "^2.0.0", "glob": "^7.2.3", - "minimatch": "^3.0.5", + "minimatch": "^10.0.1", "mkdirp": "^3.0.1", "node-fetch": "^2.7.0", "node-loader": "^2.0.0", diff --git a/Extension/src/LanguageServer/client.ts b/Extension/src/LanguageServer/client.ts index e46743815e..ce223ce0ea 100644 --- a/Extension/src/LanguageServer/client.ts +++ b/Extension/src/LanguageServer/client.ts @@ -2571,7 +2571,8 @@ export class DefaultClient implements Client { } let foundGlobMatch: boolean = false; for (const assoc in assocs) { - if (minimatch(filePath, assoc)) { + const matcher = new minimatch.Minimatch(assoc); + if (matcher.match(filePath)) { foundGlobMatch = true; break; // Assoc matched a glob pattern. } diff --git a/Extension/yarn.lock b/Extension/yarn.lock index 7a59a937de..bf3704ee7f 100644 --- a/Extension/yarn.lock +++ b/Extension/yarn.lock @@ -443,12 +443,12 @@ resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/minimatch@*": +"@types/minimatch@*", "@types/minimatch@^5.1.2": version "5.1.2" resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" integrity sha1-B1CLRXl8uB7D8nMBGwVM0HVe3co= -"@types/minimatch@^3.0.3", "@types/minimatch@^3.0.5": +"@types/minimatch@^3.0.3": version "3.0.5" resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" integrity sha1-EAHMXmo3BLg8I2An538vWOoBD0A= @@ -3271,6 +3271,13 @@ minimatch@9.0.3: dependencies: brace-expansion "^2.0.1" +minimatch@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" + integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== + dependencies: + brace-expansion "^2.0.1" + minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" From c7cdbe8274fddc977668b912183ba93825a58bf0 Mon Sep 17 00:00:00 2001 From: Colen Garoutte-Carson Date: Tue, 12 Nov 2024 17:50:29 -0800 Subject: [PATCH 6/7] Update versions of packages --- Extension/package.json | 3 +-- Extension/yarn.lock | 16 ++++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Extension/package.json b/Extension/package.json index e66eefe776..4f1f849f95 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -6517,7 +6517,6 @@ "devDependencies": { "@octokit/rest": "^20.1.1", "@types/glob": "^7.2.0", - "@types/minimatch": "^5.1.2", "@types/mocha": "^10.0.6", "@types/node": "^20.14.2", "@types/node-fetch": "^2.6.11", @@ -6569,7 +6568,7 @@ "comment-json": "^4.2.3", "escape-string-regexp": "^2.0.0", "glob": "^7.2.3", - "minimatch": "^10.0.1", + "minimatch": "^4.2.0", "mkdirp": "^3.0.1", "node-fetch": "^2.7.0", "node-loader": "^2.0.0", diff --git a/Extension/yarn.lock b/Extension/yarn.lock index bf3704ee7f..377974a1ea 100644 --- a/Extension/yarn.lock +++ b/Extension/yarn.lock @@ -443,7 +443,7 @@ resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/minimatch@*", "@types/minimatch@^5.1.2": +"@types/minimatch@*": version "5.1.2" resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" integrity sha1-B1CLRXl8uB7D8nMBGwVM0HVe3co= @@ -3271,13 +3271,6 @@ minimatch@9.0.3: dependencies: brace-expansion "^2.0.1" -minimatch@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" - integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== - dependencies: - brace-expansion "^2.0.1" - minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -3285,6 +3278,13 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimatch@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.3.tgz#b4dcece1d674dee104bb0fb833ebb85a78cbbca6" + integrity sha512-lIUdtK5hdofgCTu3aT0sOaHsYR37viUuIc0rwnnDXImbwFRcumyLMeZaM0t0I/fgxS6s6JMfu0rLD1Wz9pv1ng== + dependencies: + brace-expansion "^1.1.7" + minimatch@^5.0.1, minimatch@^5.1.0, minimatch@^5.1.6: version "5.1.6" resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" From 694ad4e750944ce543856a3c51ea6e9d76f78d52 Mon Sep 17 00:00:00 2001 From: Colen Garoutte-Carson Date: Tue, 12 Nov 2024 18:19:40 -0800 Subject: [PATCH 7/7] Add license for editorconfig-core-js due to copied code --- Extension/ThirdPartyNotices.txt | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Extension/ThirdPartyNotices.txt b/Extension/ThirdPartyNotices.txt index e7ba616c59..3dc5f2feb0 100644 --- a/Extension/ThirdPartyNotices.txt +++ b/Extension/ThirdPartyNotices.txt @@ -2429,6 +2429,7 @@ The notices below are from non-npm sources. - ANTLR (http://www.antlr2.org/) - C++11 Sublime Text Snippets (https://github.com/Rapptz/cpp-sublime-snippet) - Clang (https://clang.llvm.org/) +- editorconfig-core-js (https://github.com/editorconfig/editorconfig-core-js) - gcc-11/libgcc (https://packages.ubuntu.com/jammy/gcc-11-base) - Guidelines Support Library (https://github.com/Microsoft/GSL) - libc++ (https://libcxx.llvm.org/index.html) @@ -2677,6 +2678,31 @@ mechanisms: ========================================= END OF Clang NOTICES AND INFORMATION +%% editorconfig-core-js NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright © 2012 EditorConfig Team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +========================================= +END OF editorconfig-core-js NOTICES AND INFORMATION + %% gcc-9/libgcc NOTICES AND INFORMATION BEGIN HERE ========================================= The following runtime libraries are licensed under the terms of the