From 05f2ef44c2203317a6fc766cd0c5a9a1f241af23 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 25 Apr 2014 11:04:55 +0100 Subject: Make emoticons match only on word boundaries Change-Id: Ibe6469f8dc60e89ff87f7e65b6c35c5d50858554 diff --git a/Swift/Controllers/Chat/ChatMessageParser.cpp b/Swift/Controllers/Chat/ChatMessageParser.cpp index ce184ea..698b766 100644 --- a/Swift/Controllers/Chat/ChatMessageParser.cpp +++ b/Swift/Controllers/Chat/ChatMessageParser.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Kevin Smith + * Copyright (c) 2013-2014 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -57,12 +57,19 @@ namespace Swift { std::string regexString; /* Parse two, emoticons */ foreach (StringPair emoticon, emoticons_) { - /* Construct a regexp that finds an instance of any of the emoticons inside a group */ - regexString += regexString.empty() ? "(" : "|"; - regexString += Regex::escape(emoticon.first); + /* Construct a regexp that finds an instance of any of the emoticons inside a group + * at the start or end of the line, or beside whitespace. + */ + regexString += regexString.empty() ? "" : "|"; + std::string escaped = "(" + Regex::escape(emoticon.first) + ")"; + regexString += "^" + escaped + "|"; + regexString += escaped + "$|"; + regexString += "\\s" + escaped + "|"; + regexString += escaped + "\\s"; + } if (!regexString.empty()) { - regexString += ")"; + regexString += ""; boost::regex emoticonRegex(regexString); ChatWindow::ChatMessage newMessage; @@ -74,14 +81,22 @@ namespace Swift { const std::string& text = textPart->text; std::string::const_iterator start = text.begin(); while (regex_search(start, text.end(), match, emoticonRegex)) { - std::string::const_iterator matchStart = match[0].first; - std::string::const_iterator matchEnd = match[0].second; + int matchIndex = 0; + for (matchIndex = 1; matchIndex < static_cast(match.size()); matchIndex++) { + if (match[matchIndex].length() > 0) { + //This is the matching subgroup + break; + } + } + std::string::const_iterator matchStart = match[matchIndex].first; + std::string::const_iterator matchEnd = match[matchIndex].second; if (start != matchStart) { /* If we're skipping over plain text since the previous emoticon, record it as plain text */ newMessage.append(boost::make_shared(std::string(start, matchStart))); } boost::shared_ptr emoticonPart = boost::make_shared(); - std::map::const_iterator emoticonIterator = emoticons_.find(match.str()); + std::string matchString = match[matchIndex].str(); + std::map::const_iterator emoticonIterator = emoticons_.find(matchString); assert (emoticonIterator != emoticons_.end()); const StringPair& emoticon = *emoticonIterator; emoticonPart->imagePath = emoticon.second; diff --git a/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp index 44d7834..0a14303 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Kevin Smith + * Copyright (c) 2013-2014 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -15,6 +15,11 @@ using namespace Swift; class ChatMessageParserTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ChatMessageParserTest); CPPUNIT_TEST(testFullBody); + CPPUNIT_TEST(testOneEmoticon); + CPPUNIT_TEST(testBareEmoticon); + CPPUNIT_TEST(testHiddenEmoticon); + CPPUNIT_TEST(testEndlineEmoticon); + CPPUNIT_TEST(testBoundedEmoticons); CPPUNIT_TEST_SUITE_END(); public: @@ -38,6 +43,7 @@ public: void assertEmoticon(const ChatWindow::ChatMessage& result, size_t index, const std::string& text, const std::string& path) { boost::shared_ptr part = boost::dynamic_pointer_cast(result.getParts()[index]); + CPPUNIT_ASSERT(!!part); CPPUNIT_ASSERT_EQUAL(text, part->alternativeText); CPPUNIT_ASSERT_EQUAL(path, part->imagePath); } @@ -62,6 +68,51 @@ public: assertText(result, 9, " boom boom"); } + void testOneEmoticon() { + ChatMessageParser testling(emoticons_); + ChatWindow::ChatMessage result = testling.parseMessageBody(" :) "); + assertText(result, 0, " "); + assertEmoticon(result, 1, smile1_, smile1Path_); + assertText(result, 2, " "); + } + + + void testBareEmoticon() { + ChatMessageParser testling(emoticons_); + ChatWindow::ChatMessage result = testling.parseMessageBody(":)"); + assertEmoticon(result, 0, smile1_, smile1Path_); + } + + void testHiddenEmoticon() { + ChatMessageParser testling(emoticons_); + ChatWindow::ChatMessage result = testling.parseMessageBody("b:)a"); + assertText(result, 0, "b:)a"); + } + + void testEndlineEmoticon() { + ChatMessageParser testling(emoticons_); + ChatWindow::ChatMessage result = testling.parseMessageBody("Lazy:)"); + assertText(result, 0, "Lazy"); + assertEmoticon(result, 1, smile1_, smile1Path_); + } + + void testBoundedEmoticons() { + ChatMessageParser testling(emoticons_); + ChatWindow::ChatMessage result = testling.parseMessageBody(":)Lazy:("); + assertEmoticon(result, 0, smile1_, smile1Path_); + assertText(result, 1, "Lazy"); + assertEmoticon(result, 2, smile2_, smile2Path_); + } + + void testEmoticonParenthesis() { + ChatMessageParser testling(emoticons_); + ChatWindow::ChatMessage result = testling.parseMessageBody("(Like this :))"); + assertText(result, 0, "(Like this "); + assertEmoticon(result, 1, smile1_, smile1Path_); + assertText(result, 2, ")"); + } + + private: std::map emoticons_; std::string smile1_; -- cgit v0.10.2-6-g49f6