agent-ecosystem/docs/iterations/edit-project/iter-0-refactoring.md
iliya a58d50c749 feat: enhance project editor with autosave, improved file management, and performance optimizations
- Implemented draft autosave functionality to prevent data loss during crashes, with recovery options for unsaved changes.
- Updated file management services to support better path validation and conflict detection.
- Enhanced performance with optimized file watcher and caching strategies for project scanning.
- Improved user experience with confirmation dialogs for unsaved changes and refined keyboard shortcuts.
- Documented testing strategies and rollback plans for iterative development.
2026-02-28 14:49:41 +02:00

6.6 KiB
Raw Blame History

PR 0: Обязательные рефакторинги (R1-R4)

Перед итерацией 1. Отдельный PR.

Цель

Обязательные рефакторинги -- без них будет дублирование кода. Выполняются ДО написания нового кода. Тесты ReviewFileTree и CodeMirrorDiffView должны проходить после рефакторинга (zero behavior change).

Почему отдельный PR

R1 затрагивает production ReviewFileTree (используется в ChangeReviewDialog), R3 затрагивает production CodeMirrorDiffView. Объединение рефакторинга production-кода + 15 новых файлов в одну итерацию — чрезмерный blast radius (28 файлов). Разделение:

  • PR 0 ("Refactoring"): R1-R4 + тесты. Мёрдж только после проверки что ChangeReviewDialog работает корректно.
  • PR 1 ("Walking Skeleton"): Новые editor-файлы. Зависит от PR 0.

Рефакторинги

# Что извлечь Откуда Куда LOC
R1 buildTree() + collapse() + сортировка ReviewFileTree.tsx:42-83 src/renderer/utils/fileTreeBuilder.ts ~50
R2 getSyncLanguageExtension() + getAsyncLanguageDesc() CodeMirrorDiffView.tsx:64-128 src/renderer/utils/codemirrorLanguages.ts ~70
R3 Базовая тема CM (без diff-стилей) CodeMirrorDiffView.tsx:158-198 src/renderer/utils/codemirrorTheme.ts ~40
R4 wrapReviewHandler<T>() review.ts:133-145 src/main/ipc/ipcWrapper.ts ~15

Детали каждого рефакторинга

R1: buildTree<T>() — Generic tree builder

NB: ReviewFileTree работает с FileChangeSummary (имеет status, additions, deletions), а editor использует FileTreeEntry (имеет size, children). buildTree<T>() должен быть generic по типу node, принимая getPath: (item: T) => string и isDirectory: (item: T) => boolean как параметры.

// src/renderer/utils/fileTreeBuilder.ts
function buildTree<T>(
  items: T[],
  getPath: (item: T) => string,
  isDirectory: (item: T) => boolean
): TreeNode<T>[]

R2: getSyncLanguageExtension() — Языковой маппинг

Извлечь из CodeMirrorDiffView.tsx:64-128. 16+ языков синхронно + @codemirror/language-data async fallback.

R3: baseEditorTheme — Базовая тема

NB: diffTheme — один EditorView.theme({...}) на 125 строк. Рефакторинг:

  1. Извлечь строки 158-198 в baseEditorTheme = EditorView.theme({...}) в codemirrorTheme.ts
  2. В CodeMirrorDiffView.tsx создать const diffSpecificTheme = EditorView.theme({...}) со строками 199-283
  3. В buildExtensions() заменить diffTheme на [baseEditorTheme, diffSpecificTheme]

R4: createIpcWrapper() — Общий IPC wrapper

NB: teams.ts имеет аналогичный wrapTeamHandler (40+ вызовов), но его миграция — отдельный follow-up PR после итерации 1. Blast radius слишком высокий (1755 строк) для совмещения с основной фичей. В итерации 1 R4 применяется ТОЛЬКО к review.ts + новому editor.ts.

// src/main/ipc/ipcWrapper.ts
export function createIpcWrapper(logPrefix: string) {
  const log = createLogger(logPrefix);
  return async function wrap<T>(op: string, fn: () => Promise<T>): Promise<IpcResult<T>> {
    try { return { success: true, data: await fn() }; }
    catch (error) {
      const msg = error instanceof Error ? error.message : String(error);
      log.error(`handler error [${op}]:`, msg);
      return { success: false, error: msg };
    }
  };
}

// review.ts:
const wrapHandler = createIpcWrapper('IPC:review');

// editor.ts:
const wrapHandler = createIpcWrapper('IPC:editor');

После рефакторинга

  • ReviewFileTree.tsx импортирует buildTree, TreeNode из fileTreeBuilder.ts
  • CodeMirrorDiffView.tsx импортирует из codemirrorLanguages.ts и codemirrorTheme.ts
  • review.ts импортирует createIpcWrapper из ipcWrapper.ts
  • teams.ts — миграция wrapTeamHandlercreateIpcWrapper в отдельном follow-up PR (40+ замен, высокий blast radius)

Уровень риска по рефакторингам

R# Что трогает Уровень риска Почему
R1 ReviewFileTree (дерево файлов) Низкий Извлечение чистой функции buildTree(). Diff-viewer, hunks, approve/reject — не затрагиваются
R2 CodeMirrorDiffView (языки) Около нуля Чистые stateless функции, просто выносим в отдельный файл
R3 CodeMirrorDiffView (тема) Низкий Разрезка CSS-объекта на base + diff-specific. Визуально проверить
R4 review.ts (IPC wrapper) Около нуля 15 LOC try-catch factory, тривиальная замена

Что НЕ затрагивается: ChangeReviewDialog logic, diff rendering, hunks, approve/reject, комментарии, changeReviewSlice state.

Критерии готовности

  • pnpm typecheck проходит
  • pnpm test проходит (zero behavior change)
  • Новые unit-тесты для fileTreeBuilder.ts и ipcWrapper.ts
  • Новые unit-тесты для codemirrorLanguages.ts — проверка маппинга расширений на языки
  • Manual smoke-тест: открыть ChangeReviewDialog → файловое дерево рендерится корректно, diff подсвечивается

Оценка

  • Надёжность решения: 9/10 — все 4 рефакторинга механические (извлечение функций без изменения поведения). R2/R4 — около нуля риска, R1/R3 — низкий.
  • Уверенность: 9/10 — код review workflow не затрагивается, трогается только утилитарная часть (дерево файлов, языки, тема, wrapper).