mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
fix: take indices to messages as a hint
This commit is contained in:
parent
90211cca55
commit
e4d23fae6c
|
@ -253,21 +253,33 @@ void Channel::fillInMissingMessages(const std::vector<MessagePtr> &messages)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Channel::replaceMessage(MessagePtr message, MessagePtr replacement)
|
void Channel::replaceMessage(const MessagePtr &message,
|
||||||
|
const MessagePtr &replacement)
|
||||||
{
|
{
|
||||||
int index = this->messages_.replaceItem(message, replacement);
|
int index = this->messages_.replaceItem(message, replacement);
|
||||||
|
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
{
|
{
|
||||||
this->messageReplaced.invoke((size_t)index, replacement);
|
this->messageReplaced.invoke((size_t)index, message, replacement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Channel::replaceMessage(size_t index, MessagePtr replacement)
|
void Channel::replaceMessage(size_t index, const MessagePtr &replacement)
|
||||||
{
|
{
|
||||||
if (this->messages_.replaceItem(index, replacement))
|
MessagePtr prev;
|
||||||
|
if (this->messages_.replaceItem(index, replacement, &prev))
|
||||||
{
|
{
|
||||||
this->messageReplaced.invoke(index, replacement);
|
this->messageReplaced.invoke(index, prev, replacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Channel::replaceMessage(size_t hint, const MessagePtr &message,
|
||||||
|
const MessagePtr &replacement)
|
||||||
|
{
|
||||||
|
auto index = this->messages_.replaceItem(hint, message, replacement);
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
this->messageReplaced.invoke(hint, message, replacement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,9 @@ public:
|
||||||
pajlada::Signals::Signal<MessagePtr &, std::optional<MessageFlags>>
|
pajlada::Signals::Signal<MessagePtr &, std::optional<MessageFlags>>
|
||||||
messageAppended;
|
messageAppended;
|
||||||
pajlada::Signals::Signal<std::vector<MessagePtr> &> messagesAddedAtStart;
|
pajlada::Signals::Signal<std::vector<MessagePtr> &> messagesAddedAtStart;
|
||||||
pajlada::Signals::Signal<size_t, MessagePtr &> messageReplaced;
|
/// (index, prev-message, replacement)
|
||||||
|
pajlada::Signals::Signal<size_t, const MessagePtr &, const MessagePtr &>
|
||||||
|
messageReplaced;
|
||||||
/// Invoked when some number of messages were filled in using time received
|
/// Invoked when some number of messages were filled in using time received
|
||||||
pajlada::Signals::Signal<const std::vector<MessagePtr> &> filledInMessages;
|
pajlada::Signals::Signal<const std::vector<MessagePtr> &> filledInMessages;
|
||||||
pajlada::Signals::NoArgSignal destroyed;
|
pajlada::Signals::NoArgSignal destroyed;
|
||||||
|
@ -96,8 +98,11 @@ public:
|
||||||
|
|
||||||
void addOrReplaceTimeout(MessagePtr message);
|
void addOrReplaceTimeout(MessagePtr message);
|
||||||
void disableAllMessages();
|
void disableAllMessages();
|
||||||
void replaceMessage(MessagePtr message, MessagePtr replacement);
|
void replaceMessage(const MessagePtr &message,
|
||||||
void replaceMessage(size_t index, MessagePtr replacement);
|
const MessagePtr &replacement);
|
||||||
|
void replaceMessage(size_t index, const MessagePtr &replacement);
|
||||||
|
void replaceMessage(size_t hint, const MessagePtr &message,
|
||||||
|
const MessagePtr &replacement);
|
||||||
void deleteMessage(QString messageID);
|
void deleteMessage(QString messageID);
|
||||||
|
|
||||||
/// Removes all messages from this channel and invokes #messagesCleared
|
/// Removes all messages from this channel and invokes #messagesCleared
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
@ -212,9 +213,10 @@ public:
|
||||||
*
|
*
|
||||||
* @param[in] index the index of the item to replace
|
* @param[in] index the index of the item to replace
|
||||||
* @param[in] replacement the item to put in place of the item at index
|
* @param[in] replacement the item to put in place of the item at index
|
||||||
|
* @param[out] prev (optional) the item located at @a index before replacing
|
||||||
* @return true if a replacement took place
|
* @return true if a replacement took place
|
||||||
*/
|
*/
|
||||||
bool replaceItem(size_t index, const T &replacement)
|
bool replaceItem(size_t index, const T &replacement, T *prev = nullptr)
|
||||||
{
|
{
|
||||||
std::unique_lock lock(this->mutex_);
|
std::unique_lock lock(this->mutex_);
|
||||||
|
|
||||||
|
@ -223,10 +225,46 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prev)
|
||||||
|
{
|
||||||
|
*prev = std::exchange(this->buffer_[index], replacement);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
this->buffer_[index] = replacement;
|
this->buffer_[index] = replacement;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Replace the needle with the given item
|
||||||
|
*
|
||||||
|
* @param hint A hint on where the needle _might_ be
|
||||||
|
* @param[in] needle the item to search for
|
||||||
|
* @param[in] replacement the item to replace needle with
|
||||||
|
* @return the index of the replaced item, or -1 if no replacement took place
|
||||||
|
*/
|
||||||
|
int replaceItem(size_t hint, const T &needle, const T &replacement)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(this->mutex_);
|
||||||
|
|
||||||
|
if (hint < this->buffer_.size() && this->buffer_[hint] == needle)
|
||||||
|
{
|
||||||
|
this->buffer_[hint] = replacement;
|
||||||
|
return static_cast<int>(hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < this->buffer_.size(); ++i)
|
||||||
|
{
|
||||||
|
if (this->buffer_[i] == needle)
|
||||||
|
{
|
||||||
|
this->buffer_[i] = replacement;
|
||||||
|
return static_cast<int>(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Inserts the given item before another item
|
* @brief Inserts the given item before another item
|
||||||
*
|
*
|
||||||
|
@ -315,6 +353,32 @@ public:
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find an item with a hint
|
||||||
|
*
|
||||||
|
* @param hint A hint on where the needle _might_ be
|
||||||
|
* @param needle The item to search for
|
||||||
|
* @return the item and its index or none if it's not found
|
||||||
|
*/
|
||||||
|
std::optional<std::pair<size_t, T>> find(size_t hint, auto &&predicate)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(this->mutex_);
|
||||||
|
|
||||||
|
if (hint < this->buffer_.size() && predicate(this->buffer_[hint]))
|
||||||
|
{
|
||||||
|
return std::pair{hint, this->buffer_[hint]};
|
||||||
|
};
|
||||||
|
|
||||||
|
for (size_t i = 0; i < this->buffer_.size(); i++)
|
||||||
|
{
|
||||||
|
if (predicate(this->buffer_[i]))
|
||||||
|
{
|
||||||
|
return std::pair{i, this->buffer_[i]};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the first item matching a predicate, checking in reverse
|
* @brief Returns the first item matching a predicate, checking in reverse
|
||||||
*
|
*
|
||||||
|
|
|
@ -967,10 +967,10 @@ void ChannelView::setChannel(const ChannelPtr &underlyingChannel)
|
||||||
|
|
||||||
this->channelConnections_.managedConnect(
|
this->channelConnections_.managedConnect(
|
||||||
underlyingChannel->messageReplaced,
|
underlyingChannel->messageReplaced,
|
||||||
[this](auto index, const auto &replacement) {
|
[this](auto index, const auto &prev, const auto &replacement) {
|
||||||
if (this->shouldIncludeMessage(replacement))
|
if (this->shouldIncludeMessage(replacement))
|
||||||
{
|
{
|
||||||
this->channel_->replaceMessage(index, replacement);
|
this->channel_->replaceMessage(index, prev, replacement);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1051,8 +1051,9 @@ void ChannelView::setChannel(const ChannelPtr &underlyingChannel)
|
||||||
// on message replaced
|
// on message replaced
|
||||||
this->channelConnections_.managedConnect(
|
this->channelConnections_.managedConnect(
|
||||||
this->channel_->messageReplaced,
|
this->channel_->messageReplaced,
|
||||||
[this](size_t index, MessagePtr replacement) {
|
[this](size_t index, const MessagePtr &prev,
|
||||||
this->messageReplaced(index, replacement);
|
const MessagePtr &replacement) {
|
||||||
|
this->messageReplaced(index, prev, replacement);
|
||||||
});
|
});
|
||||||
|
|
||||||
// on messages filled in
|
// on messages filled in
|
||||||
|
@ -1258,19 +1259,21 @@ void ChannelView::messageAddedAtStart(std::vector<MessagePtr> &messages)
|
||||||
this->queueLayout();
|
this->queueLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChannelView::messageReplaced(size_t index, MessagePtr &replacement)
|
void ChannelView::messageReplaced(size_t hint, const MessagePtr &prev,
|
||||||
|
const MessagePtr &replacement)
|
||||||
{
|
{
|
||||||
auto oMessage = this->messages_.get(index);
|
auto optItem = this->messages_.find(hint, [&](const auto &it) {
|
||||||
if (!oMessage)
|
return it->getMessagePtr() == prev;
|
||||||
|
});
|
||||||
|
if (!optItem)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const auto &[index, oldItem] = *optItem;
|
||||||
auto message = *oMessage;
|
|
||||||
|
|
||||||
auto newItem = std::make_shared<MessageLayout>(replacement);
|
auto newItem = std::make_shared<MessageLayout>(replacement);
|
||||||
|
|
||||||
if (message->flags.has(MessageLayoutFlag::AlternateBackground))
|
if (oldItem->flags.has(MessageLayoutFlag::AlternateBackground))
|
||||||
{
|
{
|
||||||
newItem->flags.set(MessageLayoutFlag::AlternateBackground);
|
newItem->flags.set(MessageLayoutFlag::AlternateBackground);
|
||||||
}
|
}
|
||||||
|
@ -1278,7 +1281,7 @@ void ChannelView::messageReplaced(size_t index, MessagePtr &replacement)
|
||||||
this->scrollBar_->replaceHighlight(index,
|
this->scrollBar_->replaceHighlight(index,
|
||||||
replacement->getScrollBarHighlight());
|
replacement->getScrollBarHighlight());
|
||||||
|
|
||||||
this->messages_.replaceItem(message, newItem);
|
this->messages_.replaceItem(index, newItem);
|
||||||
this->queueLayout();
|
this->queueLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -268,7 +268,8 @@ private:
|
||||||
std::optional<MessageFlags> overridingFlags);
|
std::optional<MessageFlags> overridingFlags);
|
||||||
void messageAddedAtStart(std::vector<MessagePtr> &messages);
|
void messageAddedAtStart(std::vector<MessagePtr> &messages);
|
||||||
void messageRemoveFromStart(MessagePtr &message);
|
void messageRemoveFromStart(MessagePtr &message);
|
||||||
void messageReplaced(size_t index, MessagePtr &replacement);
|
void messageReplaced(size_t hint, const MessagePtr &prev,
|
||||||
|
const MessagePtr &replacement);
|
||||||
void messagesUpdated();
|
void messagesUpdated();
|
||||||
|
|
||||||
void performLayout(bool causedByScrollbar = false,
|
void performLayout(bool causedByScrollbar = false,
|
||||||
|
|
|
@ -114,20 +114,152 @@ TEST(LimitedQueue, PushFront)
|
||||||
|
|
||||||
TEST(LimitedQueue, ReplaceItem)
|
TEST(LimitedQueue, ReplaceItem)
|
||||||
{
|
{
|
||||||
LimitedQueue<int> queue(5);
|
LimitedQueue<int> queue(10);
|
||||||
queue.pushBack(1);
|
queue.pushBack(1);
|
||||||
queue.pushBack(2);
|
queue.pushBack(2);
|
||||||
queue.pushBack(3);
|
queue.pushBack(3);
|
||||||
|
queue.pushBack(4);
|
||||||
|
queue.pushBack(5);
|
||||||
|
queue.pushBack(6);
|
||||||
|
|
||||||
int idex = queue.replaceItem(2, 10);
|
int idex = queue.replaceItem(2, 10);
|
||||||
EXPECT_EQ(idex, 1);
|
EXPECT_EQ(idex, 1);
|
||||||
idex = queue.replaceItem(5, 11);
|
idex = queue.replaceItem(7, 11);
|
||||||
EXPECT_EQ(idex, -1);
|
EXPECT_EQ(idex, -1);
|
||||||
|
|
||||||
bool res = queue.replaceItem(std::size_t(0), 9);
|
int prev = -1;
|
||||||
|
bool res = queue.replaceItem(std::size_t(0), 9, &prev);
|
||||||
EXPECT_TRUE(res);
|
EXPECT_TRUE(res);
|
||||||
res = queue.replaceItem(std::size_t(5), 4);
|
EXPECT_EQ(prev, 1);
|
||||||
|
res = queue.replaceItem(std::size_t(6), 4);
|
||||||
EXPECT_FALSE(res);
|
EXPECT_FALSE(res);
|
||||||
|
|
||||||
SNAPSHOT_EQUALS(queue.getSnapshot(), {9, 10, 3}, "first snapshot");
|
// correct hint
|
||||||
|
EXPECT_EQ(queue.replaceItem(3ULL, 4, 11), 3);
|
||||||
|
// incorrect hints
|
||||||
|
EXPECT_EQ(queue.replaceItem(5ULL, 11, 12), 3);
|
||||||
|
EXPECT_EQ(queue.replaceItem(0ULL, 12, 13), 3);
|
||||||
|
// oob hint
|
||||||
|
EXPECT_EQ(queue.replaceItem(42ULL, 13, 14), 3);
|
||||||
|
// bad needle
|
||||||
|
EXPECT_EQ(queue.replaceItem(0ULL, 15, 16), -1);
|
||||||
|
|
||||||
|
SNAPSHOT_EQUALS(queue.getSnapshot(), {9, 10, 3, 14, 5, 6},
|
||||||
|
"first snapshot");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LimitedQueue, Find)
|
||||||
|
{
|
||||||
|
LimitedQueue<int> queue(10);
|
||||||
|
queue.pushBack(1);
|
||||||
|
queue.pushBack(2);
|
||||||
|
queue.pushBack(3);
|
||||||
|
queue.pushBack(4);
|
||||||
|
queue.pushBack(5);
|
||||||
|
queue.pushBack(6);
|
||||||
|
|
||||||
|
// without hint
|
||||||
|
EXPECT_FALSE(queue
|
||||||
|
.find([](int i) {
|
||||||
|
return i == 0;
|
||||||
|
})
|
||||||
|
.has_value());
|
||||||
|
EXPECT_EQ(queue
|
||||||
|
.find([](int i) {
|
||||||
|
return i == 1;
|
||||||
|
})
|
||||||
|
.value(),
|
||||||
|
1);
|
||||||
|
EXPECT_EQ(queue
|
||||||
|
.find([](int i) {
|
||||||
|
return i == 2;
|
||||||
|
})
|
||||||
|
.value(),
|
||||||
|
2);
|
||||||
|
EXPECT_EQ(queue
|
||||||
|
.find([](int i) {
|
||||||
|
return i == 6;
|
||||||
|
})
|
||||||
|
.value(),
|
||||||
|
6);
|
||||||
|
EXPECT_FALSE(queue
|
||||||
|
.find([](int i) {
|
||||||
|
return i == 7;
|
||||||
|
})
|
||||||
|
.has_value());
|
||||||
|
EXPECT_FALSE(queue
|
||||||
|
.find([](int i) {
|
||||||
|
return i > 6;
|
||||||
|
})
|
||||||
|
.has_value());
|
||||||
|
EXPECT_FALSE(queue
|
||||||
|
.find([](int i) {
|
||||||
|
return i <= 0;
|
||||||
|
})
|
||||||
|
.has_value());
|
||||||
|
|
||||||
|
// with hint
|
||||||
|
EXPECT_FALSE(queue
|
||||||
|
.find(0,
|
||||||
|
[](int i) {
|
||||||
|
return i == 0;
|
||||||
|
})
|
||||||
|
.has_value());
|
||||||
|
// correct hint
|
||||||
|
EXPECT_EQ(queue
|
||||||
|
.find(0,
|
||||||
|
[](int i) {
|
||||||
|
return i == 1;
|
||||||
|
})
|
||||||
|
.value(),
|
||||||
|
(std::pair{0ULL, 1}));
|
||||||
|
EXPECT_EQ(queue
|
||||||
|
.find(1,
|
||||||
|
[](int i) {
|
||||||
|
return i == 2;
|
||||||
|
})
|
||||||
|
.value(),
|
||||||
|
(std::pair{1ULL, 2}));
|
||||||
|
// incorrect hint
|
||||||
|
EXPECT_EQ(queue
|
||||||
|
.find(1,
|
||||||
|
[](int i) {
|
||||||
|
return i == 1;
|
||||||
|
})
|
||||||
|
.value(),
|
||||||
|
(std::pair{0ULL, 1}));
|
||||||
|
EXPECT_EQ(queue
|
||||||
|
.find(5,
|
||||||
|
[](int i) {
|
||||||
|
return i == 6;
|
||||||
|
})
|
||||||
|
.value(),
|
||||||
|
(std::pair{5ULL, 6}));
|
||||||
|
// oob hint
|
||||||
|
EXPECT_EQ(queue
|
||||||
|
.find(6,
|
||||||
|
[](int i) {
|
||||||
|
return i == 3;
|
||||||
|
})
|
||||||
|
.value(),
|
||||||
|
(std::pair{2ULL, 3}));
|
||||||
|
// non-existent items
|
||||||
|
EXPECT_FALSE(queue
|
||||||
|
.find(42,
|
||||||
|
[](int i) {
|
||||||
|
return i == 7;
|
||||||
|
})
|
||||||
|
.has_value());
|
||||||
|
EXPECT_FALSE(queue
|
||||||
|
.find(0,
|
||||||
|
[](int i) {
|
||||||
|
return i > 6;
|
||||||
|
})
|
||||||
|
.has_value());
|
||||||
|
EXPECT_FALSE(queue
|
||||||
|
.find(0,
|
||||||
|
[](int i) {
|
||||||
|
return i <= 0;
|
||||||
|
})
|
||||||
|
.has_value());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue