(kept: ParseEntry[], originalCmd: string)
| 1193 | } |
| 1194 | |
| 1195 | function reconstructCommand(kept: ParseEntry[], originalCmd: string): string { |
| 1196 | if (!kept.length) return originalCmd |
| 1197 | |
| 1198 | let result = '' |
| 1199 | let cmdSubDepth = 0 |
| 1200 | let inProcessSub = false |
| 1201 | |
| 1202 | for (let i = 0; i < kept.length; i++) { |
| 1203 | const part = kept[i] |
| 1204 | const prev = kept[i - 1] |
| 1205 | const next = kept[i + 1] |
| 1206 | |
| 1207 | // Handle strings |
| 1208 | if (typeof part === 'string') { |
| 1209 | // For strings containing command separators (|&;), use double quotes to make them unambiguous |
| 1210 | // For other strings (spaces, etc), use shell-quote's quote() which handles escaping correctly |
| 1211 | const hasCommandSeparator = /[|&;]/.test(part) |
| 1212 | const str = hasCommandSeparator |
| 1213 | ? `"${part}"` |
| 1214 | : needsQuoting(part) |
| 1215 | ? quote([part]) |
| 1216 | : part |
| 1217 | |
| 1218 | // Check if this string ends with $ and next is ( |
| 1219 | const endsWithDollar = str.endsWith('$') |
| 1220 | const nextIsParen = |
| 1221 | next && typeof next === 'object' && 'op' in next && next.op === '(' |
| 1222 | |
| 1223 | // Special spacing rules |
| 1224 | const noSpace = |
| 1225 | result.endsWith('(') || // After opening paren |
| 1226 | prev === '$' || // After standalone $ |
| 1227 | (typeof prev === 'object' && prev && 'op' in prev && prev.op === ')') // After closing ) |
| 1228 | |
| 1229 | // Special case: add space after <( |
| 1230 | if (result.endsWith('<(')) { |
| 1231 | result += ' ' + str |
| 1232 | } else { |
| 1233 | result = addToken(result, str, noSpace) |
| 1234 | } |
| 1235 | |
| 1236 | // If string ends with $ and next is (, don't add space after |
| 1237 | if (endsWithDollar && nextIsParen) { |
| 1238 | // Mark that we should not add space before next ( |
| 1239 | } |
| 1240 | continue |
| 1241 | } |
| 1242 | |
| 1243 | // Handle operators |
| 1244 | if (typeof part !== 'object' || !part || !('op' in part)) continue |
| 1245 | const op = part.op as string |
| 1246 | |
| 1247 | // Handle glob patterns |
| 1248 | if (op === 'glob' && 'pattern' in part) { |
| 1249 | result = addToken(result, part.pattern as string) |
| 1250 | continue |
| 1251 | } |
| 1252 |
no test coverage detected