Keep every line of a multi-line message in chat history

The text log has a message's line breaks in it, but history was read one
line at a time, so every line after the first failed to parse and was
dropped. A line that starts no entry of its own now continues the
message before it, which also recovers what earlier versions wrote.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 10:50:12 +00:00
parent 8e9c461424
commit f9b2f4d7cb
2 changed files with 42 additions and 11 deletions

View File

@@ -175,13 +175,33 @@ public final class ChatLogStore {
return entries;
}
/**
* A message is written as it was sent, so one with line breaks spans several lines of the
* log: a line that starts no entry of its own continues the message before it.
*/
private static List<String> readTextMessages(LogFiles files, ChatLogScope scope) throws IOException {
if (!Files.isRegularFile(files.txt)) return Collections.emptyList();
List<String> entries = new ArrayList<>();
Matcher message = null;
StringBuilder text = new StringBuilder();
for (String line : Files.readAllLines(files.txt, StandardCharsets.UTF_8)) {
String entry = textLineToHtml(line, scope);
if (entry != null) entries.add(entry);
Matcher m = TEXT_MESSAGE.matcher(line);
boolean begin = line.startsWith("*** ");
if (!m.matches() && !begin) {
if (message != null) text.append('\n').append(line);
continue;
}
if (message != null) entries.add(messageLineHtml(message, text, scope));
message = null;
if (begin) {
entries.add(beginLineHtml(line));
} else {
message = m;
text.setLength(0);
text.append(m.group(3));
}
}
if (message != null) entries.add(messageLineHtml(message, text, scope));
return entries;
}
@@ -190,14 +210,12 @@ public final class ChatLogStore {
return new ArrayList<>(entries.subList(entries.size() - limit, entries.size()));
}
private static String textLineToHtml(String line, ChatLogScope scope) {
if (line == null || line.isBlank()) return null;
if (line.startsWith("*** ")) {
return "<div class=\"TextMessage_BeginHistory\"><span class=\"Body\">"
+ BBCode.escape(line) + "</span></div>";
}
Matcher m = TEXT_MESSAGE.matcher(line);
if (!m.matches()) return null;
private static String beginLineHtml(String line) {
return "<div class=\"TextMessage_BeginHistory\"><span class=\"Body\">"
+ BBCode.escape(line) + "</span></div>";
}
private static String messageLineHtml(Matcher m, CharSequence text, ChatLogScope scope) {
String target = switch (scope) {
case PRIVATE -> "Private";
case CHANNEL -> "Channel";
@@ -206,7 +224,8 @@ public final class ChatLogStore {
return "<div class=\"TextMessage_Message Incoming Neutral " + target + "\"><span class=\"Body\">"
+ "<span class=\"TextMessage_Time\">&lt;" + m.group(1) + "&gt;</span> "
+ "<span class=\"TextMessage_UserLink\">&quot;" + BBCode.escape(m.group(2)) + "&quot;</span>: "
+ "<span class=\"TextMessage_Text\">" + BBCode.toHtml(m.group(3)) + "</span></span></div>";
+ "<span class=\"TextMessage_Text\">" + BBCode.toHtml(text.toString().stripTrailing())
+ "</span></span></div>";
}
private static String beginHtml(String kind, LocalDateTime timestamp) {

View File

@@ -68,4 +68,16 @@ class ChatLogStoreTest {
assertTrue(capped.truncated());
assertTrue(capped.entries().get(9).contains("filler " + (ChatLogStore.HISTORY_LIMIT - 1)));
}
@Test
void readsAMessageWithLineBreaksBackWhole() throws Exception {
ChatLogStore logs = new ChatLogStore(temp, "+Tyg2JtxE8vRNZp+JiUBnmBh0MY=");
logs.appendMessage(ChatLogScope.CHANNEL, false, 5, "uid", "alice", "first line\n\nthird line");
logs.appendMessage(ChatLogScope.CHANNEL, false, 5, "uid", "bob", "reply");
List<String> history = logs.channelHtmlHistory();
assertEquals(2, history.size());
assertTrue(history.get(0).contains("first line<br><br>third line"), history.get(0));
assertTrue(history.get(1).contains("reply"));
}
}