App: text editor undo với stack, Command và Memento

Xây text editor có undo redo: Command đóng gói thao tác, Memento snapshot, stack lịch sử, macro và benchmark bộ nhớ.
KhóDesign pattern ~120 phút

Giới thiệu

Bài này xây editor có undo redo. Gõ sai bấm undo là quay lại ngay.

Kiến thức cần nhớ

Command gói thao tác

  • Mỗi thao tác là 1 lệnh có execute và unexecute.
  • Insert lưu chữ đã chèn, Delete lưu chữ đã xóa.
  • Ví dụ đời thường: mỗi bước đi ghi vào sổ để xóa được.
  • Lệnh mới sau undo sẽ xóa sạch redo.

2 stack undo redo

  • Execute đẩy vào stack undo và xóa stack redo.
  • Undo pop ra đảo ngược rồi đẩy sang redo.
  • Redo pop ra làm lại rồi đẩy về undo.
  • Giới hạn 500 entry để RAM không phình.

Memento và macro

  • Memento chụp ảnh toàn văn cho thao tác lớn như paste 5000 chữ.
  • Undo việc lớn chỉ cần khôi phục ảnh 1 bước.
  • Macro gom 5 thao tác thành 1 lệnh undo 1 lần.
  • Delta nhỏ tiết kiệm RAM, snapshot lớn tiện 1 bước.

Ví dụ

  • Input: gõ hello rồi xóa 2 chữ. Output: còn hel, undo về hello, redo về hel.
  • Giải thích: mỗi lệnh nhớ đủ delta để đi tới lui.
  • Input: paste 5000 chữ rồi undo. Output: về đúng văn bản cũ chỉ 1 bước.
  • Giải thích: snapshot lưu ảnh trước paste nên khôi phục 1 lần.

💻 Bài tập

Ngôn ngữ: C++17 (hoặc Python). Dùng Command ngày 81, Memento ngày 95. Nộp vào day-148-editor/.

Bài 1: Văn bản và lệnh

class Document {
 public:
  void insert(size_t pos, const std::string &s);
  std::string erase(size_t pos, size_t len);   // trả phần đã xóa
  const std::string &text() const; size_t cursor() const; void setCursor(size_t);
};
struct EditCommand { virtual void execute(Document&) = 0; virtual void undo(Document&) = 0;
                     virtual bool mergeWith(const EditCommand &next) { return false; } };
LệnhLưu gì để undo
InsertCommand(pos, text)Vị trí và chuỗi đã chèn
DeleteCommand(pos, len)Chuỗi đã xóa (lấy lúc execute)
ReplaceCommand(pos, len, text)Chuỗi cũ và chuỗi mới

Gộp gõ phím: 10 lần gõ từng ký tự liền nhau (cùng vị trí nối tiếp, cách nhau dưới 1 giây) gộp thành 1 InsertCommand. Dùng mergeWith.

Bài 2: Lịch sử undo/redo

type "Hello"; type " World"; undo -> "Hello"; redo -> "Hello World"
undo; type "!"; redo -> không làm gì, văn bản "Hello!"
Lịch sử giới hạn 500 mục, mục cũ nhất bị bỏ
Undo khi lịch sử rỗng -> không làm gì
  1. MacroCommand: "Tìm và thay tất cả" là 1 macro, undo 1 lần là hoàn tác toàn bộ.
  2. Paste đoạn trên 5000 ký tự: lưu Memento (ảnh toàn văn bản) thay vì lệnh. Undo khôi phục ảnh.
  3. In bộ nhớ lịch sử ước lượng sau 10.000 thao tác.

Bài 3: Giao diện dòng lệnh (nâng cao)

  1. REPL với lệnh: i <pos> <text>, d <pos> <len>, r <find> <replace>, u, y (redo), p (in), h (in lịch sử, đánh dấu vị trí hiện tại).
  2. Lưu và mở file. Đánh dấu "đã sửa" khi lịch sử khác điểm lưu gần nhất (undo về đúng điểm lưu thì hết dấu).
  3. Test tính chất: chuỗi 1000 thao tác ngẫu nhiên, rồi undo hết phải về văn bản ban đầu, rồi redo hết phải về văn bản cuối.

✅ Tiêu chí hoàn thành

  • Test mẫu undo/redo pass.
  • Gõ phím liền nhau gộp thành 1 bước undo.
  • Test tính chất 1000 thao tác ngẫu nhiên pass.
  • Dấu "đã sửa" đúng khi undo về điểm lưu.

❓ Câu hỏi ôn tập

  • Command cần lưu gì để đảo ngược được?
  • 2 stack undo redo chuyển lệnh cho nhau thế nào?
  • Memento dùng cho thao tác nào?
  • Macro gom nhiều lệnh để làm gì?

🌕 Bạn đã hoàn thành ngày 148. Làm hết bài tập phía trên rồi sang ngày tiếp theo.

Bớt paste mù, hiểu sâu từng dòng mình viết.

180 Days Challenge. 180 ngày cai nghiện vibe code.