Round-seven fixes: swap name room for .tmp; input and notice details

- The shortened fallback swap name was capped at exactly NAME_MAX, so
  compaction's '<journal>.tmp' failed with ENAMETOOLONG on every
  checkpoint and the journal grew by a full copy of the buffer each
  time. Cap it at NAME_MAX - 4.
- A lone UTF-8 lead byte (8-bit Meta terminals) waited a second for
  continuation bytes on every key; wait 250 ms. An invalid byte now ends
  a pending ESC/C-k prefix instead of leaving it for the next key.
- Journal notices are shown after a deferred open's own status (which
  used to overwrite them), all pending ones at once, and the lock-out
  notice is not repeated after every save while the lock is still held.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
This commit is contained in:
Claude
2026-09-24 09:16:04 -07:00
committed by Kyle Isom
parent 78d78f6f54
commit b9f91b0ed2
6 changed files with 65 additions and 20 deletions
+16 -2
View File
@@ -1471,6 +1471,12 @@ TEST(Audit_Swap_FailuresReachTheUser)
sb.Flush(&b);
ASSERT_TRUE(sb.TakeUserNotice().find("another kte") != std::string::npos);
ASSERT_EQ(sb.TakeUserNotice(), std::string());
// Saving resets the journal and retries the lock; still held by A, but
// the user was already told.
sb.ResetJournal(b);
b.insert_text(0, 0, "b");
sb.Flush(&b);
ASSERT_EQ(sb.TakeUserNotice(), std::string());
b.SetSwapRecorder(nullptr);
sb.Detach(&b, false);
a.SetSwapRecorder(nullptr);
@@ -1503,17 +1509,25 @@ TEST(Audit_Swap_LongBasenameStillJournaled)
const std::string path = (d.path / (std::string(240, 'n') + ".txt")).string();
std::ofstream(path) << "base\n";
const std::string swp = kte::SwapManager::ComputeSwapPathForFilename(path);
ASSERT_TRUE(std::filesystem::path(swp).filename().string().size() <= 255);
// Room for compaction's "<journal>.tmp" too.
ASSERT_TRUE(std::filesystem::path(swp).filename().string().size() + 4 <= 255);
std::filesystem::remove(swp);
Buffer a;
std::string err;
ASSERT_TRUE(a.OpenFromFile(path, err));
kte::SwapManager sm;
kte::SwapConfig cfg;
cfg.checkpoint_bytes = 100;
cfg.compact_bytes = 1000;
sm.SetConfig(cfg);
sm.Attach(&a);
a.SetSwapRecorder(sm.RecorderFor(&a));
a.insert_text(0, 0, "X");
for (int i = 0; i < 50; ++i)
a.insert_text(0, 0, std::string(200, 'X'));
sm.Flush(&a);
ASSERT_TRUE(std::filesystem::exists(swp));
// Compaction works: the journal holds about one checkpoint, not fifty.
ASSERT_TRUE(std::filesystem::file_size(swp) < 40000);
a.SetSwapRecorder(nullptr);
sm.Detach(&a, true);
ASSERT_TRUE(!std::filesystem::exists(swp));