aboutsummaryrefslogtreecommitdiff
path: root/src/chat.cpp
diff options
context:
space:
mode:
authorJude Melton-Houghton <jwmhjwmh@gmail.com>2023-01-14 16:14:37 -0500
committerGitHub <noreply@github.com>2023-01-14 16:14:37 -0500
commit2f9f0c09001b8fbc6f4559cbfdccf0261f1377a4 (patch)
treea6fe6efb95c6f6144110aee95b4694219281e143 /src/chat.cpp
parent8fded9d990d1e1142b630b24c96f1a2c959fef36 (diff)
downloadminetest-2f9f0c09001b8fbc6f4559cbfdccf0261f1377a4.tar.xz
Improve chat history (#12975)
Diffstat (limited to 'src/chat.cpp')
-rw-r--r--src/chat.cpp122
1 files changed, 83 insertions, 39 deletions
diff --git a/src/chat.cpp b/src/chat.cpp
index 9483fb96e..b021a3e6b 100644
--- a/src/chat.cpp
+++ b/src/chat.cpp
@@ -454,9 +454,36 @@ ChatPrompt::ChatPrompt(const std::wstring &prompt, u32 history_limit):
{
}
+const std::wstring &ChatPrompt::getLineRef() const
+{
+ return m_history_index >= m_history.size() ? m_line : m_history[m_history_index].line;
+}
+
+std::wstring &ChatPrompt::makeLineRef()
+{
+ if (m_history_index >= m_history.size()) {
+ return m_line;
+ } else {
+ if (!m_history[m_history_index].saved)
+ m_history[m_history_index].saved = m_history[m_history_index].line;
+ return m_history[m_history_index].line;
+ }
+}
+
+bool ChatPrompt::HistoryEntry::operator==(const ChatPrompt::HistoryEntry &other)
+{
+ if (line != other.line)
+ return false;
+ if (saved == other.saved)
+ return true;
+ if ((!saved || saved == line) && (!other.saved || other.saved == other.line))
+ return true;
+ return false;
+}
+
void ChatPrompt::input(wchar_t ch)
{
- m_line.insert(m_cursor, 1, ch);
+ makeLineRef().insert(m_cursor, 1, ch);
m_cursor++;
clampView();
m_nick_completion_start = 0;
@@ -465,7 +492,7 @@ void ChatPrompt::input(wchar_t ch)
void ChatPrompt::input(const std::wstring &str)
{
- m_line.insert(m_cursor, str);
+ makeLineRef().insert(m_cursor, str);
m_cursor += str.size();
clampView();
m_nick_completion_start = 0;
@@ -474,22 +501,38 @@ void ChatPrompt::input(const std::wstring &str)
void ChatPrompt::addToHistory(const std::wstring &line)
{
+ std::wstring old_line = getLine();
+ if (m_history_index < m_history.size()) {
+ auto entry = m_history.begin() + m_history_index;
+ if (entry->saved && entry->line == line) {
+ entry->line = *entry->saved;
+ entry->saved = nullopt;
+ // Remove potential duplicates
+ auto dup_before = std::find(m_history.begin(), entry, *entry);
+ if (dup_before != entry)
+ m_history.erase(dup_before);
+ else if (std::find(entry + 1, m_history.end(), *entry) != m_history.end())
+ m_history.erase(entry);
+ }
+ }
if (!line.empty() &&
- (m_history.size() == 0 || m_history.back() != line)) {
+ (m_history.size() == 0 || m_history.back().line != line)) {
+ HistoryEntry entry(line);
// Remove all duplicates
- m_history.erase(std::remove(m_history.begin(), m_history.end(),
- line), m_history.end());
+ m_history.erase(std::remove(m_history.begin(), m_history.end(), entry),
+ m_history.end());
// Push unique line
- m_history.push_back(line);
+ m_history.push_back(std::move(entry));
}
if (m_history.size() > m_history_limit)
m_history.erase(m_history.begin());
m_history_index = m_history.size();
+ m_line = std::move(old_line);
}
void ChatPrompt::clear()
{
- m_line.clear();
+ makeLineRef().clear();
m_view = 0;
m_cursor = 0;
m_nick_completion_start = 0;
@@ -498,8 +541,8 @@ void ChatPrompt::clear()
std::wstring ChatPrompt::replace(const std::wstring &line)
{
- std::wstring old_line = m_line;
- m_line = line;
+ std::wstring old_line = getLine();
+ makeLineRef() = line;
m_view = m_cursor = line.size();
clampView();
m_nick_completion_start = 0;
@@ -509,24 +552,23 @@ std::wstring ChatPrompt::replace(const std::wstring &line)
void ChatPrompt::historyPrev()
{
- if (m_history_index != 0)
- {
+ if (m_history_index != 0) {
--m_history_index;
- replace(m_history[m_history_index]);
+ m_view = m_cursor = getLineRef().size();
+ clampView();
+ m_nick_completion_start = 0;
+ m_nick_completion_end = 0;
}
}
void ChatPrompt::historyNext()
{
- if (m_history_index + 1 >= m_history.size())
- {
- m_history_index = m_history.size();
- replace(L"");
- }
- else
- {
- ++m_history_index;
- replace(m_history[m_history_index]);
+ if (m_history_index < m_history.size()) {
+ m_history_index++;
+ m_view = m_cursor = getLineRef().size();
+ clampView();
+ m_nick_completion_start = 0;
+ m_nick_completion_end = 0;
}
}
@@ -541,6 +583,7 @@ void ChatPrompt::nickCompletion(const std::list<std::string>& names, bool backwa
// m_nick_completion_start..m_nick_completion_end are the
// interval where the originally used prefix was. Cycle
// through the list of completions of that prefix.
+ const std::wstring &line = getLineRef();
u32 prefix_start = m_nick_completion_start;
u32 prefix_end = m_nick_completion_end;
bool initial = (prefix_end == 0);
@@ -548,14 +591,14 @@ void ChatPrompt::nickCompletion(const std::list<std::string>& names, bool backwa
{
// no previous nick completion is active
prefix_start = prefix_end = m_cursor;
- while (prefix_start > 0 && !iswspace(m_line[prefix_start-1]))
+ while (prefix_start > 0 && !iswspace(line[prefix_start-1]))
--prefix_start;
- while (prefix_end < m_line.size() && !iswspace(m_line[prefix_end]))
+ while (prefix_end < line.size() && !iswspace(line[prefix_end]))
++prefix_end;
if (prefix_start == prefix_end)
return;
}
- std::wstring prefix = m_line.substr(prefix_start, prefix_end - prefix_start);
+ std::wstring prefix = line.substr(prefix_start, prefix_end - prefix_start);
// find all names that start with the selected prefix
std::vector<std::wstring> completions;
@@ -576,9 +619,9 @@ void ChatPrompt::nickCompletion(const std::list<std::string>& names, bool backwa
u32 replacement_index = 0;
if (!initial)
{
- while (word_end < m_line.size() && !iswspace(m_line[word_end]))
+ while (word_end < line.size() && !iswspace(line[word_end]))
++word_end;
- std::wstring word = m_line.substr(prefix_start, word_end - prefix_start);
+ std::wstring word = line.substr(prefix_start, word_end - prefix_start);
// cycle through completions
for (u32 i = 0; i < completions.size(); ++i)
@@ -595,12 +638,12 @@ void ChatPrompt::nickCompletion(const std::list<std::string>& names, bool backwa
}
}
std::wstring replacement = completions[replacement_index];
- if (word_end < m_line.size() && iswspace(m_line[word_end]))
+ if (word_end < line.size() && iswspace(line[word_end]))
++word_end;
// replace existing word with replacement word,
// place the cursor at the end and record the completion prefix
- m_line.replace(prefix_start, word_end - prefix_start, replacement);
+ makeLineRef().replace(prefix_start, word_end - prefix_start, replacement);
m_cursor = prefix_start + replacement.size();
clampView();
m_nick_completion_start = prefix_start;
@@ -616,7 +659,7 @@ void ChatPrompt::reformat(u32 cols)
}
else
{
- s32 length = m_line.size();
+ s32 length = getLineRef().size();
bool was_at_end = (m_view + m_cols >= length + 1);
m_cols = cols - m_prompt.size();
if (was_at_end)
@@ -627,7 +670,7 @@ void ChatPrompt::reformat(u32 cols)
std::wstring ChatPrompt::getVisiblePortion() const
{
- return m_prompt + m_line.substr(m_view, m_cols);
+ return m_prompt + getLineRef().substr(m_view, m_cols);
}
s32 ChatPrompt::getVisibleCursorPosition() const
@@ -640,7 +683,8 @@ void ChatPrompt::cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope sco
s32 old_cursor = m_cursor;
s32 new_cursor = m_cursor;
- s32 length = m_line.size();
+ const std::wstring &line = getLineRef();
+ s32 length = line.size();
s32 increment = (dir == CURSOROP_DIR_RIGHT) ? 1 : -1;
switch (scope) {
@@ -650,17 +694,17 @@ void ChatPrompt::cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope sco
case CURSOROP_SCOPE_WORD:
if (dir == CURSOROP_DIR_RIGHT) {
// skip one word to the right
- while (new_cursor < length && iswspace(m_line[new_cursor]))
+ while (new_cursor < length && iswspace(line[new_cursor]))
new_cursor++;
- while (new_cursor < length && !iswspace(m_line[new_cursor]))
+ while (new_cursor < length && !iswspace(line[new_cursor]))
new_cursor++;
- while (new_cursor < length && iswspace(m_line[new_cursor]))
+ while (new_cursor < length && iswspace(line[new_cursor]))
new_cursor++;
} else {
// skip one word to the left
- while (new_cursor >= 1 && iswspace(m_line[new_cursor - 1]))
+ while (new_cursor >= 1 && iswspace(line[new_cursor - 1]))
new_cursor--;
- while (new_cursor >= 1 && !iswspace(m_line[new_cursor - 1]))
+ while (new_cursor >= 1 && !iswspace(line[new_cursor - 1]))
new_cursor--;
}
break;
@@ -680,10 +724,10 @@ void ChatPrompt::cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope sco
break;
case CURSOROP_DELETE:
if (m_cursor_len > 0) { // Delete selected text first
- m_line.erase(m_cursor, m_cursor_len);
+ makeLineRef().erase(m_cursor, m_cursor_len);
} else {
m_cursor = MYMIN(new_cursor, old_cursor);
- m_line.erase(m_cursor, abs(new_cursor - old_cursor));
+ makeLineRef().erase(m_cursor, abs(new_cursor - old_cursor));
}
m_cursor_len = 0;
break;
@@ -707,7 +751,7 @@ void ChatPrompt::cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope sco
void ChatPrompt::clampView()
{
- s32 length = m_line.size();
+ s32 length = getLineRef().size();
if (length + 1 <= m_cols)
{
m_view = 0;