reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

commit 7df629fa23ac2be9fec50a2c47057a532e03aa05
parent cfff6ea8fa9f2b1e319ec0ba2d2a77b245aad869
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Fri,  8 Apr 2016 23:22:53 -0400

implement smarter filtering in the browser

Diffstat:
Msrc/browser.cpp | 7+++----
Msrc/browser.hpp | 3++-
Asrc/filter.cpp | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/filter.hpp | 35+++++++++++++++++++++++++++++++++++
Atest/filter.cpp | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 190 insertions(+), 5 deletions(-)

diff --git a/src/browser.cpp b/src/browser.cpp @@ -311,8 +311,8 @@ void Browser::checkFilter() const string &filter = from_autostring(wideFilter); - if(filter != m_filter) { - m_filter = filter; + if(filter != m_filter.get()) { + m_filter.set(filter); fillList(); } } @@ -578,8 +578,7 @@ bool Browser::match(const Entry &entry) const const string &category = getValue(CategoryColumn, entry); const string &author = getValue(AuthorColumn, entry); - return icontains(name, m_filter) || icontains(category, m_filter) || - icontains(author, m_filter); + return m_filter.match(name) || m_filter.match(category) || m_filter.match(author); } auto Browser::getEntry(const int listIndex) const -> const Entry * diff --git a/src/browser.hpp b/src/browser.hpp @@ -20,6 +20,7 @@ #include "dialog.hpp" +#include "filter.hpp" #include "listview.hpp" #include "registry.hpp" @@ -121,7 +122,7 @@ private: int m_currentIndex; int m_filterTimer; - std::string m_filter; + Filter m_filter; std::vector<Entry> m_entries; std::vector<size_t> m_visibleEntries; std::map<const Entry *, const Version *> m_actions; diff --git a/src/filter.cpp b/src/filter.cpp @@ -0,0 +1,73 @@ +/* ReaPack: Package manager for REAPER + * Copyright (C) 2015-2016 Christian Fillion + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "filter.hpp" + +#include <boost/algorithm/string.hpp> + +using namespace std; + +void Filter::set(const string &input) +{ + enum State { Default, DoubleQuote, SingleQuote }; + + string buf; + State state = Default; + m_input = input; + + m_words.clear(); + + auto push = [&] { + state = Default; + + if(!buf.empty()) { + m_words.push_back(buf); + buf.clear(); + } + }; + + for(const char c : input) { + if(c == '"' && state != SingleQuote) { + if(state == DoubleQuote) + push(); + else + state = DoubleQuote; + } + else if(c == '\'' && state != DoubleQuote) { + if(state == SingleQuote) + push(); + else + state = SingleQuote; + } + else if(c == '\x20' && state == Default) + push(); + else + buf += c; + } + + push(); +} + +bool Filter::match(const string &str) const +{ + for(const string &word : m_words) { + if(!boost::icontains(str, word)) + return false; + } + + return true; +} diff --git a/src/filter.hpp b/src/filter.hpp @@ -0,0 +1,35 @@ +/* ReaPack: Package manager for REAPER + * Copyright (C) 2015-2016 Christian Fillion + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef REAPACK_FILTER_HPP +#define REAPACK_FILTER_HPP + +#include <string> +#include <vector> + +class Filter { +public: + const std::string get() const { return m_input; } + void set(const std::string &); + bool match(const std::string &) const; + +private: + std::string m_input; + std::vector<std::string> m_words; +}; + +#endif diff --git a/test/filter.cpp b/test/filter.cpp @@ -0,0 +1,77 @@ +#include <catch.hpp> + +#include <filter.hpp> + +using namespace std; + +static const char *M = "[filter]"; + +TEST_CASE("basic matching", M) { + Filter f; + REQUIRE(f.match("world")); + + f.set("hello"); + + REQUIRE(f.match("hello")); + REQUIRE(f.match("HELLO")); + REQUIRE_FALSE(f.match("world")); +} + +TEST_CASE("get/set filter", M) { + Filter f; + REQUIRE(f.get().empty()); + + f.set("hello"); + REQUIRE(f.get() == "hello"); + REQUIRE(f.match("hello")); + + f.set("world"); + REQUIRE(f.get() == "world"); + REQUIRE(f.match("world")); +} + +TEST_CASE("word matching", M) { + Filter f; + f.set("hello world"); + + REQUIRE_FALSE(f.match("hello")); + REQUIRE(f.match("hello world")); + REQUIRE(f.match("helloworld")); + REQUIRE(f.match("hello test world")); +} + +TEST_CASE("double quote matching", M) { + Filter f; + f.set("\"hello world\""); + + REQUIRE(f.match("hello world")); + REQUIRE_FALSE(f.match("helloworld")); + REQUIRE_FALSE(f.match("hello test world")); +} + +TEST_CASE("single quote matching", M) { + Filter f; + f.set("'hello world'"); + + REQUIRE(f.match("hello world")); + REQUIRE_FALSE(f.match("helloworld")); + REQUIRE_FALSE(f.match("hello test world")); +} + +TEST_CASE("mixing quotes", M) { + Filter f; + + SECTION("double in single") { + f.set("'hello \"world\"'"); + + REQUIRE(f.match("hello \"world\"")); + REQUIRE_FALSE(f.match("hello world")); + } + + SECTION("single in double") { + f.set("\"hello 'world'\""); + + REQUIRE(f.match("hello 'world'")); + REQUIRE_FALSE(f.match("hello world")); + } +}