import 'dart:io'; import 'package:path/path.dart' as p; import 'file_ops.dart' show isSameOrInside, samePath; import 'vfs.dart' show isDav, vExists, vJoin; /// 약속 (E5c): 탐색기 "목록에서 숨기기" 의 목록 규칙 (파일은 그대로 - 앱 설정의 경로 목록). /// - [isHidden]: 목록 · 경로 → 숨긴 것인지 (Windows 대소문자 무시, 같은 목록이면 열쇠를 다시 만들지 않음). /// - [follow]: 옮기거나 이름을 바꾼 것 (원래 → 새 경로, 그 안의 것도) 은 새 경로로, 지운 것 (그 안의 것도 - 실제로 없어진 /// 것만, [exists] 로 확인) 은 목록에서 뺀다 (관리자 결정 10/18). 출력 = 새 목록, 바뀐 것이 없으면 null. 같은 경로는 한 번만. /// - 다시 쓰는 곳: 탐색기 트리 · Rsync 트리 · 찾기 결과. class HiddenList { List? _from; Set _keys = const {}; /// 숨긴 목록에서 같은 경로를 찾는 열쇠 static String keyOf(String x) { if (isDav(x) || x.contains('://')) return x; final n = p.normalize(x); return Platform.isWindows ? n.toLowerCase() : n; } bool isHidden(List list, String path) { if (list.isEmpty) return false; if (!identical(list, _from)) { _from = list; _keys = {for (final x in list) keyOf(x)}; } return _keys.contains(keyOf(path)); } static Future?> follow(List list, {Iterable<(String, String)> moved = const [], Iterable gone = const [], Future Function(String path) exists = vExists}) async { if (list.isEmpty || (moved.isEmpty && gone.isEmpty)) return null; final out = []; var changed = false; for (final h in list) { if (gone.any((g) => isSameOrInside(h, g)) && !await exists(h)) { changed = true; continue; } var now = h; for (final (from, to) in moved) { if (samePath(h, from)) { now = to; break; } if (isSameOrInside(h, from)) { now = vJoin(to, isDav(h) ? h.substring(from.length).replaceAll('\\', '/').replaceFirst(RegExp('^/+'), '') : p.relative(h, from: from)); break; } } if (now != h) changed = true; if (!out.any((x) => keyOf(x) == keyOf(now))) out.add(now); } return changed ? out : null; } }