Files
clide/legacy/clide/controllers/todos.py
T
jpmschweitzerandClaude Opus 4.7 a355751437 move python clide to legacy/
Clide is being rebuilt as a Flutter desktop app. The Python Textual
implementation moves wholesale into legacy/ rather than being deleted:
its pane model, panel set, git skills, and panel communication design
are real thought that should remain readable next to the new code
while the rebuild finds its shape. Git's rename tracking preserves
history, so `git log -- legacy/` still works.

The Flutter rebuild lives at the repo root alongside a Go sidecar
(the architecture claudian was heading toward, which folds into
clide as a core component rather than a separate plugin project).
Bootstrap of the new shape lands in subsequent commits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 20:30:51 +02:00

164 lines
4.4 KiB
Python

"""TODOs controller for tracking TODO comments."""
from pathlib import Path
from textual.message import Message
from clide.controllers.base import controller
from clide.models.todos import (
ProjectTodoItem,
TodoItem,
TodosState,
TodosSummary,
TodoType,
)
from clide.services.todo_scanner import TodoScanner
@controller
class TodosController:
"""Controller for TODO/FIXME comment tracking."""
class TodosUpdated(Message):
"""Emitted when TODOs list is updated."""
def __init__(
self,
items: list[TodoItem],
project_items: list[ProjectTodoItem],
summary: TodosSummary,
) -> None:
self.items = items
self.project_items = project_items
self.summary = summary
super().__init__()
class TodoSelected(Message):
"""Emitted when a TODO is selected."""
def __init__(self, item: TodoItem) -> None:
self.item = item
super().__init__()
def __init__(self, project_path: Path) -> None:
self._scanner = TodoScanner(project_path)
self._state = TodosState()
@property
def state(self) -> TodosState:
"""Get TODOs state."""
return self._state
@property
def items(self) -> list[TodoItem]:
"""Get list of code TODO items."""
return self._state.items
@property
def project_items(self) -> list[ProjectTodoItem]:
"""Get list of project TODO items from TODO.md."""
return self._state.project_items
@property
def summary(self) -> TodosSummary:
"""Get TODOs summary."""
return self._state.summary
@property
def total_count(self) -> int:
"""Get total code TODO count."""
return self._state.summary.total
@property
def project_count(self) -> int:
"""Get total project TODO count."""
return self._state.summary.project_total
async def refresh(
self,
) -> tuple[list[TodoItem], list[ProjectTodoItem], TodosSummary]:
"""Refresh TODOs from project.
Returns:
Tuple of (code items, project items, summary)
"""
items, project_items, summary = await self._scanner.scan()
self._state.items = items
self._state.project_items = project_items
self._state.summary = summary
return items, project_items, summary
def filter_by_type(self, todo_type: TodoType | None) -> list[TodoItem]:
"""Filter TODOs by type.
Args:
todo_type: Type to filter by, or None for all
Returns:
Filtered list of TODOs
"""
self._state.filter_type = todo_type
if todo_type is None:
return self._state.items
return self._state.items_by_type(todo_type)
def get_items_for_file(self, path: Path) -> list[TodoItem]:
"""Get TODOs for a specific file.
Args:
path: File path
Returns:
List of TODOs for that file
"""
return self._state.items_for_file(path)
def select_item(self, index: int) -> TodoItem | None:
"""Select a TODO by index.
Args:
index: Item index
Returns:
Selected item or None
"""
if 0 <= index < len(self._state.items):
self._state.selected_index = index
return self._state.items[index]
return None
def toggle_group_by_file(self) -> bool:
"""Toggle grouping by file.
Returns:
New group_by_file value
"""
self._state.group_by_file = not self._state.group_by_file
return self._state.group_by_file
def get_grouped_items(self) -> dict[Path, list[TodoItem]]:
"""Get TODOs grouped by file.
Returns:
Dictionary mapping file paths to TODO lists
"""
grouped: dict[Path, list[TodoItem]] = {}
for item in self._state.items:
if item.file_path not in grouped:
grouped[item.file_path] = []
grouped[item.file_path].append(item)
return grouped
async def scan(self) -> tuple[list[TodoItem], list[ProjectTodoItem]]:
"""Scan for TODOs and return items.
Returns:
Tuple of (code items, project items)
"""
items, project_items, _ = await self.refresh()
return items, project_items