1const LEFT_BRACKETS = ['(', '{'] as const; 2const RIGHT_BRACKETS = [')', '}'] as const; 3 4type LeftBracket = (typeof LEFT_BRACKETS)[number]; 5type RightBracket = (typeof RIGHT_BRACKETS)[number]; 6type Bracket = LeftBracket | RightBracket; 7 8export function findMatchingBracketPosition( 9 contents: string, 10 bracket: Bracket, 11 offset: number = 0 12): number { 13 // search first occurrence of `bracket` 14 const firstBracketPos = contents.indexOf(bracket, offset); 15 if (firstBracketPos < 0) { 16 return -1; 17 } 18 19 let stackCounter = 0; 20 const matchingBracket = getMatchingBracket(bracket); 21 22 if (isLeftBracket(bracket)) { 23 const contentsLength = contents.length; 24 // search forward 25 for (let i = firstBracketPos + 1; i < contentsLength; ++i) { 26 const c = contents[i]; 27 if (c === bracket) { 28 stackCounter += 1; 29 } else if (c === matchingBracket) { 30 if (stackCounter === 0) { 31 return i; 32 } 33 stackCounter -= 1; 34 } 35 } 36 } else { 37 // search backward 38 for (let i = firstBracketPos - 1; i >= 0; --i) { 39 const c = contents[i]; 40 if (c === bracket) { 41 stackCounter += 1; 42 } else if (c === matchingBracket) { 43 if (stackCounter === 0) { 44 return i; 45 } 46 stackCounter -= 1; 47 } 48 } 49 } 50 51 return -1; 52} 53 54function isLeftBracket(bracket: Bracket): boolean { 55 const leftBracketList: readonly Bracket[] = LEFT_BRACKETS; 56 return leftBracketList.includes(bracket); 57} 58 59function getMatchingBracket(bracket: Bracket): Bracket { 60 switch (bracket) { 61 case '(': 62 return ')'; 63 case ')': 64 return '('; 65 case '{': 66 return '}'; 67 case '}': 68 return '{'; 69 default: 70 throw new Error(`Unsupported bracket - ${bracket}`); 71 } 72} 73