Run indexing with streaming progress display. Exits on failure.
(project_root: str)
| 184 | |
| 185 | |
| 186 | def _run_index_with_progress(project_root: str) -> None: |
| 187 | """Run indexing with streaming progress display. Exits on failure.""" |
| 188 | from rich.console import Console as _Console |
| 189 | from rich.live import Live as _Live |
| 190 | from rich.spinner import Spinner as _Spinner |
| 191 | |
| 192 | from . import client as _client |
| 193 | |
| 194 | err_console = _Console(stderr=True) |
| 195 | last_progress_line: str | None = None |
| 196 | |
| 197 | with _Live(_Spinner("dots", "Indexing..."), console=err_console, transient=True) as live: |
| 198 | |
| 199 | def _on_waiting() -> None: |
| 200 | live.update( |
| 201 | _Spinner( |
| 202 | "dots", |
| 203 | "Another indexing is ongoing, waiting for it to finish...", |
| 204 | ) |
| 205 | ) |
| 206 | |
| 207 | def _on_progress(progress: IndexingProgress) -> None: |
| 208 | nonlocal last_progress_line |
| 209 | last_progress_line = f"Indexing: {_format_progress(progress)}" |
| 210 | live.update(_Spinner("dots", last_progress_line)) |
| 211 | |
| 212 | try: |
| 213 | resp = _client.index(project_root, on_progress=_on_progress, on_waiting=_on_waiting) |
| 214 | except RuntimeError as e: |
| 215 | live.stop() |
| 216 | # Let DaemonStartError propagate to the decorator for consistent handling. |
| 217 | if isinstance(e, _client.DaemonStartError): |
| 218 | raise |
| 219 | _typer.echo(f"Indexing failed: {e}", err=True) |
| 220 | raise _typer.Exit(code=1) |
| 221 | |
| 222 | # Print the final progress line so it remains visible after the spinner clears |
| 223 | if last_progress_line is not None: |
| 224 | _typer.echo(last_progress_line, err=True) |
| 225 | |
| 226 | if not resp.success: |
| 227 | _typer.echo(f"Indexing failed: {resp.message}", err=True) |
| 228 | raise _typer.Exit(code=1) |
| 229 | |
| 230 | |
| 231 | def _search_with_wait_spinner( |