* Parses Quarto cell options from #| lines.
(content: string)
| 172 | * Parses Quarto cell options from #| lines. |
| 173 | */ |
| 174 | function parseQuartoCellOptions(content: string): QuartoCellOptions | undefined { |
| 175 | const lines = content.split('\n') |
| 176 | const optionLines = lines.filter(line => line.trimStart().startsWith('#|')) |
| 177 | |
| 178 | if (optionLines.length === 0) { |
| 179 | return undefined |
| 180 | } |
| 181 | |
| 182 | const options: QuartoCellOptions = {} |
| 183 | const raw: Record<string, unknown> = {} |
| 184 | |
| 185 | for (const line of optionLines) { |
| 186 | const match = /^#\|\s*(\S+):\s*(.*)$/.exec(line.trimStart()) |
| 187 | if (match) { |
| 188 | const key = match[1] |
| 189 | let value: string | boolean | number = match[2].trim() |
| 190 | |
| 191 | // Handle quoted strings |
| 192 | if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) { |
| 193 | value = value.slice(1, -1) |
| 194 | } else if (value === 'true') { |
| 195 | value = true |
| 196 | } else if (value === 'false') { |
| 197 | value = false |
| 198 | } else { |
| 199 | // Try to parse as number, but only if it's finite |
| 200 | const num = Number(value) |
| 201 | if (!Number.isNaN(num) && Number.isFinite(num)) { |
| 202 | value = num |
| 203 | } |
| 204 | // Otherwise keep as string |
| 205 | } |
| 206 | |
| 207 | // Map to typed properties with runtime type validation |
| 208 | switch (key) { |
| 209 | case 'label': |
| 210 | if (typeof value === 'string') { |
| 211 | options.label = value |
| 212 | } else { |
| 213 | raw[key] = value |
| 214 | } |
| 215 | break |
| 216 | case 'echo': |
| 217 | if (typeof value === 'boolean') { |
| 218 | options.echo = value |
| 219 | } else if (value === 'true' || value === 'false') { |
| 220 | options.echo = value === 'true' |
| 221 | } else { |
| 222 | raw[key] = value |
| 223 | } |
| 224 | break |
| 225 | case 'eval': |
| 226 | if (typeof value === 'boolean') { |
| 227 | options.eval = value |
| 228 | } else if (value === 'true' || value === 'false') { |
| 229 | options.eval = value === 'true' |
| 230 | } else { |
| 231 | raw[key] = value |