├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── appveyor.yml ├── install_libcxx.sh └── src ├── include └── dummy.h ├── lib ├── CMakeLists.txt └── dummy.cpp └── test ├── CMakeLists.txt └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | TAGS 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "contrib/testinator"] 2 | path = contrib/testinator 3 | url = https://github.com/elbeno/testinator.git 4 | [submodule "contrib/gsl"] 5 | path = contrib/gsl 6 | url = https://github.com/Microsoft/GSL.git 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # adapted from https://github.com/ericniebler/range-v3 2 | 3 | language: cpp 4 | script: cmake 5 | 6 | matrix: 7 | include: 8 | - env: CLANG_VERSION=3.4 BUILD_TYPE=Debug CPP=1y SAN=On LIBCXX=On 9 | os: linux 10 | addons: &clang34 11 | apt: 12 | packages: 13 | - util-linux 14 | - clang-3.4 15 | sources: 16 | - ubuntu-toolchain-r-test 17 | - llvm-toolchain-precise-3.4 18 | 19 | - env: CLANG_VERSION=3.4 BUILD_TYPE=Release CPP=1y SAN=On LIBCXX=On 20 | os: linux 21 | addons: *clang34 22 | 23 | - env: CLANG_VERSION=3.7 BUILD_TYPE=Debug CPP=14 SAN=Off LIBCXX=On 24 | os: linux 25 | addons: &clang37 26 | apt: 27 | packages: 28 | - util-linux 29 | - clang-3.7 30 | sources: 31 | - ubuntu-toolchain-r-test 32 | - llvm-toolchain-precise-3.7 33 | 34 | - env: CLANG_VERSION=3.7 BUILD_TYPE=Release CPP=14 SAN=Off LIBCXX=On 35 | os: linux 36 | addons: *clang37 37 | 38 | # - env: GCC_VERSION=4.9 BUILD_TYPE=Debug CPP=14 SAN=Off LIBCXX=Off 39 | # os: linux 40 | # addons: &gcc49 41 | # apt: 42 | # packages: 43 | # - g++-4.9 44 | # sources: 45 | # - ubuntu-toolchain-r-test 46 | 47 | # - env: GCC_VERSION=4.9 BUILD_TYPE=Release CPP=14 SAN=Off LIBCXX=Off 48 | # os: linux 49 | # addons: *gcc49 50 | 51 | - env: GCC_VERSION=5 BUILD_TYPE=Debug CPP=14 SAN=Off LIBCXX=Off 52 | os: linux 53 | addons: &gcc5 54 | apt: 55 | packages: 56 | - g++-5 57 | sources: 58 | - ubuntu-toolchain-r-test 59 | 60 | - env: GCC_VERSION=5 BUILD_TYPE=Release CPP=14 SAN=Off LIBCXX=Off 61 | os: linux 62 | addons: *gcc5 63 | 64 | before_install: 65 | - export CHECKOUT_PATH=`pwd`; 66 | - if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi 67 | - if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi 68 | - if [ "$CLANG_VERSION" == "3.4" ]; then export CXX="/usr/local/clang-3.4/bin/clang++" CC="/usr/local/clang-3.4/bin/clang"; fi 69 | - if [ "$LIBCXX" == "On" ]; then sudo CXX=$CXX CC=$CC ./install_libcxx.sh; fi 70 | 71 | install: 72 | - cd $CHECKOUT_PATH 73 | 74 | - if [ ! -d build ]; then mkdir build; fi 75 | - cd build 76 | 77 | - export CXX_FLAGS="" 78 | - export CXX_LINKER_FLAGS="" 79 | 80 | - if [ -z "$BUILD_TYPE" ]; then export BUILD_TYPE=Debug; fi 81 | 82 | - if [ -n "$CLANG_VERSION" ]; then CXX_FLAGS="${CXX_FLAGS} -D__extern_always_inline=inline"; fi 83 | - if [ "$LIBCXX" == "On" ]; then CXX_FLAGS="${CXX_FLAGS} -stdlib=libc++ -I/usr/include/c++/v1/"; fi 84 | - if [ "$LIBCXX" == "On" ]; then CXX_LINKER_FLAGS="${CXX_FLAGS} -L/usr/lib/ -lc++"; fi 85 | 86 | - cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" -DCMAKE_EXE_LINKER_FLAGS="${CXX_LINKER_FLAGS}" -DCXX_STD=$CPP 87 | - make VERBOSE=1 88 | 89 | script: 90 | - if [ "$BUILD_TYPE" == "Debug" ]; then ctest -VV --schedule-random; fi 91 | 92 | notifications: 93 | email: false 94 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8) 2 | project (autocomplete) 3 | 4 | # Includes for this project 5 | include_directories ("${PROJECT_SOURCE_DIR}/src/include") 6 | 7 | # Include testinator and the GSL 8 | include_directories ("${PROJECT_SOURCE_DIR}/contrib/testinator/src/include") 9 | include_directories ("${PROJECT_SOURCE_DIR}/contrib/gsl/include") 10 | 11 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 12 | 13 | # Default C++ standard: C++14 14 | if(CXX_STD) 15 | else() 16 | set(CXX_STD 14) 17 | endif() 18 | 19 | # Set up tests 20 | enable_testing() 21 | include(CTest) 22 | 23 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 24 | set(MY_CXX_FLAGS_LIST 25 | ) 26 | string(REPLACE ";" " " MY_CXX_FLAGS "${MY_CXX_FLAGS_LIST}") 27 | 28 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MY_CXX_FLAGS}") 29 | else() 30 | set(MY_CXX_FLAGS_LIST 31 | -ftemplate-backtrace-limit=0 32 | -ffunction-sections 33 | -Wall -Wextra -Werror -pedantic-errors 34 | -Wcast-align 35 | -Wcast-qual 36 | -Wctor-dtor-privacy 37 | -Wdisabled-optimization 38 | -Wformat=2 39 | -Winit-self 40 | -Wmissing-include-dirs 41 | # -Wold-style-cast 42 | -Woverloaded-virtual 43 | -Wredundant-decls 44 | # -Wshadow 45 | # -Wsign-conversion 46 | -Wsign-promo 47 | -Wstrict-overflow=5 48 | -Wswitch-default 49 | -Wundef 50 | ) 51 | string(REPLACE ";" " " MY_CXX_FLAGS "${MY_CXX_FLAGS_LIST}") 52 | 53 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++${CXX_STD} ${MY_CXX_FLAGS}") 54 | 55 | # Debug/Release 56 | set(CMAKE_CXX_FLAGS_DEBUG "-O0 -fno-inline -g3 -fstack-protector-all") 57 | set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -g0 -march=native -mtune=native -DNDEBUG") 58 | set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_DEBUG} -fprofile-arcs -ftest-coverage") 59 | endif() 60 | 61 | # Clang/GCC specifics 62 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 63 | if(SAN) 64 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address,undefined,integer -fno-omit-frame-pointer -fno-sanitize=unsigned-integer-overflow") 65 | endif() 66 | elseif(CMAKE_COMPILER_IS_GNUCXX) 67 | endif() 68 | 69 | # Pipe separate tests into ctest 70 | # Adapted from https://github.com/ChaiScript/ChaiScript/blob/develop/CMakeLists.txt 71 | macro(ADD_INDIVIDUAL_TESTS executable type suffix) 72 | set(test_path $ENV{PATH}) 73 | get_target_property(target_files ${executable} SOURCES) 74 | foreach(source ${target_files}) 75 | string(REGEX MATCH .*cpp source "${source}") 76 | if(source) 77 | file(READ "${source}" contents) 78 | string(REGEX MATCHALL "DEF_${type}[ ]*[(][ ]*[^, ]+[ ]*,[ ]*[^,) ]+[ ]*[),]" found_tests ${contents}) 79 | foreach(hit ${found_tests}) 80 | string(REGEX REPLACE "DEF_${type}[ ]*[(][ ]*([^, ]+)[ ]*,[ ]*[^,) ]+[ ]*[),]" "\\1" tname ${hit}) 81 | string(REGEX REPLACE "DEF_${type}[ ]*[(][ ]*[^, ]+[ ]*,[ ]*([^,) ]+)[ ]*[),]" "\\1" sname ${hit}) 82 | set(test_name ${executable}.${sname}.${tname}${suffix}) 83 | add_test(NAME ${test_name} 84 | COMMAND "${executable}" --testName=${tname}${suffix} --suiteName=${sname}) 85 | set_tests_properties(${test_name} PROPERTIES TIMEOUT 30 ENVIRONMENT "PATH=${test_path}") 86 | endforeach() 87 | endif() 88 | endforeach() 89 | endmacro() 90 | 91 | macro(ADD_TESTINATOR_TESTS executable) 92 | ADD_INDIVIDUAL_TESTS(${executable} "TEST" "") 93 | ADD_INDIVIDUAL_TESTS(${executable} "TIMED_TEST" "") 94 | ADD_INDIVIDUAL_TESTS(${executable} "PROPERTY" "Property") 95 | ADD_INDIVIDUAL_TESTS(${executable} "COMPLEXITY_PROPERTY" "ComplexityProperty") 96 | endmacro() 97 | 98 | add_subdirectory (src/lib) 99 | add_subdirectory (src/test) 100 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbeno/autocomplete/287bde2f4d5a3eb98e9ecb5733a75397f3e37dd8/LICENSE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | What's interesting here is not the actual autocompletion (I just implemented a 2 | couple of simple methods of autocompletion, one using vectors and one using 3 | ternary trees). 4 | 5 | The more interesting thing is that the Completer provides an interface where one 6 | of the methods can either be implemented (presumably for efficiency's sake) by 7 | the underlying engine, or (if not provided) can fall back to being satisfied in 8 | terms of other methods. This is achieved by the usual methods of SFINAE (which 9 | may improve in the future). 10 | 11 | Good interfaces should be complete, expressive and efficient: this approach 12 | allows completeness and expressiveness to vary independently of efficiency 13 | (which can be taken arbitrarily far by some implementation). The approach is 14 | similar to that taken by many Haskell typeclasses. 15 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | platform: 2 | - x86 3 | - x64 4 | 5 | configuration: 6 | - Debug 7 | - Release 8 | 9 | os: Visual Studio 2015 10 | 11 | clone_folder: c:\projects\autocomplete 12 | 13 | install: 14 | - git submodule update --init --recursive 15 | 16 | build_script: 17 | # show settings 18 | - cmake -version 19 | - echo %platform% 20 | - echo %configuration% 21 | 22 | # generate a solution file 23 | - cd c:\projects\autocomplete 24 | - mkdir build 25 | - cd build 26 | - if "%platform%" == "x64" set cmake_platform=%platform% 27 | - cmake -g "Visual Studio 14 2015" .. -DCMAKE_GENERATOR_PLATFORM=%cmake_platform% 28 | 29 | # build it 30 | - if "%platform%" == "x86" set msbuild_platform=Win32 31 | - if "%platform%" == "x64" set msbuild_platform=%platform% 32 | - msbuild autocomplete.sln /p:Configuration=%configuration% /toolsversion:14.0 /p:PlatformToolset=v140 /p:Platform=%msbuild_platform% 33 | 34 | test_script: 35 | - if "%configuration%" == "Debug" ctest -VV --schedule-random -C Debug 36 | -------------------------------------------------------------------------------- /install_libcxx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Install libc++ under travis 4 | # Copied from https://github.com/ericniebler/range-v3 5 | 6 | svn --quiet co http://llvm.org/svn/llvm-project/libcxx/trunk libcxx 7 | mkdir libcxx/build 8 | (cd libcxx/build && cmake .. -DLIBCXX_CXX_ABI=libstdc++ -DLIBCXX_CXX_ABI_INCLUDE_PATHS="/usr/include/c++/4.6;/usr/include/c++/4.6/x86_64-linux-gnu") 9 | make -C libcxx/build cxx -j2 10 | sudo cp libcxx/build/lib/libc++.so.1.0 /usr/lib/ 11 | sudo cp -r libcxx/build/include/c++/v1 /usr/include/c++/v1/ 12 | sudo ln -sf /usr/lib/libc++.so.1.0 /usr/lib/libc++.so 13 | sudo ln -sf /usr/lib/libc++.so.1.0 /usr/lib/libc++.so.1 14 | -------------------------------------------------------------------------------- /src/include/dummy.h: -------------------------------------------------------------------------------- 1 | int dummy(); 2 | -------------------------------------------------------------------------------- /src/lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library (${PROJECT_NAME} dummy) 2 | -------------------------------------------------------------------------------- /src/lib/dummy.cpp: -------------------------------------------------------------------------------- 1 | int dummy() 2 | { 3 | return 1; 4 | } 5 | -------------------------------------------------------------------------------- /src/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable (test_${PROJECT_NAME} main.cpp) 2 | target_link_libraries(test_${PROJECT_NAME} ${PROJECT_NAME}) 3 | ADD_TESTINATOR_TESTS (test_${PROJECT_NAME}) 4 | -------------------------------------------------------------------------------- /src/test/main.cpp: -------------------------------------------------------------------------------- 1 | #define TESTINATOR_MAIN 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace std; 15 | 16 | // ----------------------------------------------------------------------------- 17 | // SFINAE member/functionality detection 18 | 19 | #ifndef _MSC_VER 20 | template 21 | using void_t = void; 22 | #endif 23 | 24 | #define SFINAE_DETECT(name, expr) \ 25 | template \ 26 | using name##_t = decltype(expr); \ 27 | template \ 28 | struct has_##name : public std::false_type {}; \ 29 | template \ 30 | struct has_##name>> : public std::true_type {}; 31 | 32 | //------------------------------------------------------------------------------ 33 | 34 | // detect CommonPrefix(string) 35 | SFINAE_DETECT(common_prefix, 36 | declval().CommonPrefix(std::string())) 37 | 38 | // detect AddWords(It, It) 39 | SFINAE_DETECT(add_words, 40 | declval().AddWords(0, 0)) 41 | 42 | //------------------------------------------------------------------------------ 43 | 44 | template 45 | class Completer 46 | { 47 | 48 | public: 49 | // Add a word to the autocomplete corpus. 50 | void AddWord(const string& s) 51 | { 52 | e.AddWord(s); 53 | } 54 | 55 | // Add several words to the autocomplete corpus. 56 | template 57 | void AddWords(InputIt first, InputIt last) 58 | { 59 | AddWords(first, last, has_add_words{}); 60 | } 61 | 62 | // Add several words to the autocomplete corpus. 63 | void AddWords(const std::initializer_list& il) 64 | { 65 | AddWords(std::cbegin(il), std::cend(il)); 66 | } 67 | 68 | // Get the candidates for autocompletion for a given prefix. 69 | vector GetCandidates(const string& prefix) 70 | { 71 | return e.GetCandidates(prefix); 72 | } 73 | 74 | // Get the prefix common to all candidates. This can be computed as the common 75 | // prefix of all the candidates, but may also be more efficiently computed 76 | // depending on the engine. 77 | string CommonPrefix(const string& prefix) 78 | { 79 | return CommonPrefix(prefix, has_common_prefix{}); 80 | } 81 | 82 | private: 83 | template 84 | void AddWords(InputIt first, InputIt last, std::true_type) 85 | { 86 | e.AddWords(first, last); 87 | } 88 | 89 | template 90 | void AddWords(InputIt first, InputIt last, std::false_type) 91 | { 92 | while (first != last) { 93 | e.AddWord(*first++); 94 | } 95 | } 96 | 97 | string CommonPrefix(const string& prefix, std::true_type) 98 | { 99 | return e.CommonPrefix(prefix); 100 | } 101 | 102 | string CommonPrefix(const string& prefix, std::false_type) 103 | { 104 | auto v = GetCandidates(prefix); 105 | if (v.empty()) return prefix; 106 | if (v.size() == 1) return v[0]; 107 | 108 | // more than one candidate: accumulate the common prefix 109 | auto init = std::make_pair(v[0].cbegin(), v[0].cend()); 110 | using P = decltype(init); 111 | auto p = std::accumulate(v.cbegin() + 1, v.cend(), init, 112 | [] (const P& p, const string& s) { 113 | auto newp = std::mismatch(p.first, p.second, 114 | s.cbegin(), s.cend()); 115 | if (std::distance(p.first, newp.first) 116 | < std::distance(p.first, p.second)) 117 | return P{ p.first, newp.first }; 118 | return p; 119 | }); 120 | return string{p.first, p.second}; 121 | } 122 | 123 | Engine e; 124 | }; 125 | 126 | //------------------------------------------------------------------------------ 127 | 128 | #undef SFINAE_DETECT 129 | 130 | //------------------------------------------------------------------------------ 131 | 132 | // A simple vector-based completion engine. Doesn't provide CommonPrefix. 133 | class VectorEngine 134 | { 135 | public: 136 | void AddWord(const string& s) 137 | { 138 | auto i = std::lower_bound(words.cbegin(), words.cend(), s); 139 | words.insert(i, s); 140 | } 141 | 142 | template 143 | void AddWords(It first, It last) 144 | { 145 | AddWords(first, last, typename std::iterator_traits::iterator_category()); 146 | } 147 | 148 | template 149 | void AddWords(RandomIt first, RandomIt last, std::random_access_iterator_tag) 150 | { 151 | auto s = words.size(); 152 | words.reserve(s + static_cast(last - first)); 153 | AddWords(first, last, std::input_iterator_tag{}); 154 | } 155 | 156 | template 157 | void AddWords(InputIt first, InputIt last, std::input_iterator_tag) 158 | { 159 | while (first != last) { 160 | words.push_back(*first++); 161 | } 162 | std::sort(words.begin(), words.end()); 163 | } 164 | 165 | vector GetCandidates(const string& prefix) 166 | { 167 | auto i = std::lower_bound(words.cbegin(), words.cend(), prefix); 168 | vector v; 169 | auto is_prefix_of = [] (const string& p, const string& w) { 170 | return std::mismatch(p.cbegin(), p.cend(), 171 | w.cbegin(), w.cend()).first == p.cend(); 172 | }; 173 | while (i != words.cend() && is_prefix_of(prefix, *i)) 174 | { 175 | v.push_back(*i++); 176 | } 177 | if (v.empty()) return { prefix }; 178 | return v; 179 | } 180 | 181 | private: 182 | vector words; 183 | }; 184 | 185 | // A more complex ternary tree based completion engine. Provides an 186 | // implementation of CommonPrefix. 187 | class TernaryTreeEngine 188 | { 189 | public: 190 | void AddWord(const string& s) 191 | { 192 | Node** n = &tree; 193 | for (auto it = s.cbegin(); ;) 194 | { 195 | if (!*n) { 196 | *n = new Node(*it); 197 | } 198 | if (*it < (*n)->value) { 199 | n = &((*n)->left); 200 | } else if (*it == (*n)->value) { 201 | ++it; 202 | if (it == s.cend()) { 203 | (*n)->wordend = true; 204 | break; 205 | } 206 | n = &((*n)->center); 207 | } else { 208 | n = &((*n)->right); 209 | } 210 | } 211 | } 212 | 213 | vector GetCandidates(const string& prefix) 214 | { 215 | string s; 216 | const Node* n; 217 | std::tie(s, n) = TraversePrefix(prefix, tree); 218 | if (!n) return { prefix }; 219 | return WordsFrom(n, s); 220 | } 221 | 222 | string CommonPrefix(const string& prefix) 223 | { 224 | string s; 225 | const Node* n; 226 | std::tie(s, n) = TraversePrefix(prefix, tree); 227 | 228 | // fill in the rest of the unambiguous prefix 229 | while (n && !n->left && !n->right) { 230 | s.push_back(n->value); 231 | if (n->wordend) break; 232 | n = n->center; 233 | } 234 | 235 | if (s.size() < prefix.size()) return prefix; 236 | return s; 237 | } 238 | 239 | ~TernaryTreeEngine() { delete tree; } 240 | 241 | private: 242 | struct Node 243 | { 244 | Node(char c) : value(c) {} 245 | ~Node() { delete left; delete center; delete right; } 246 | Node* left = nullptr; 247 | Node* center = nullptr; 248 | Node* right = nullptr; 249 | char value; 250 | bool wordend = false; 251 | }; 252 | 253 | // Traverse the tree up to the point directed by the prefix. Return the 254 | // resulting string and the node reached. 255 | std::pair TraversePrefix(const string& prefix, const Node* n) 256 | { 257 | string s; 258 | auto it = prefix.cbegin(); 259 | while (n && it != prefix.cend()) { 260 | if (*it < n->value) { 261 | n = n->left; 262 | } else if (*it == n->value) { 263 | s.push_back(n->value); 264 | ++it; 265 | n = n->center; 266 | } else { 267 | n = n->right; 268 | } 269 | } 270 | return {s, n}; 271 | } 272 | 273 | // Given a node and a prefix, return all the words that can result from that 274 | // starting point. 275 | vector WordsFrom(const Node* n, string& s) 276 | { 277 | auto v = [&, this] () -> vector { 278 | if (!n->left) return {}; 279 | string sl{s}; 280 | return WordsFrom(n->left, sl); 281 | }(); 282 | 283 | auto vr = [&, this] () -> vector { 284 | if (!n->right) return {}; 285 | string sr{s}; 286 | return WordsFrom(n->right, sr); 287 | }(); 288 | 289 | if (n->center || n->wordend) s.push_back(n->value); 290 | if (n->wordend) v.push_back(s); 291 | 292 | auto vc = [&, this] () -> vector { 293 | if (!n->center) return {}; 294 | return WordsFrom(n->center, s); 295 | }(); 296 | 297 | move(vc.begin(), vc.end(), back_inserter(v)); 298 | move(vr.begin(), vr.end(), back_inserter(v)); 299 | return v; 300 | } 301 | 302 | Node* tree = nullptr; 303 | }; 304 | 305 | //------------------------------------------------------------------------------ 306 | 307 | DEF_TEST(VectorCandidates, VectorEngine) 308 | { 309 | Completer c; 310 | c.AddWord("commit"); 311 | c.AddWord("cherry"); 312 | c.AddWord("cherry-pick"); 313 | 314 | vector expected = { "cherry", "cherry-pick" }; 315 | auto v = c.GetCandidates("ch"); 316 | return std::is_permutation(v.cbegin(), v.cend(), 317 | expected.cbegin(), expected.cend()); 318 | } 319 | 320 | DEF_TEST(VectorAllCandidates, VectorEngine) 321 | { 322 | Completer c; 323 | c.AddWord("commit"); 324 | c.AddWord("cherry"); 325 | c.AddWord("cherry-pick"); 326 | 327 | vector expected = { "commit" ,"cherry", "cherry-pick" }; 328 | auto v = c.GetCandidates(""); 329 | return std::is_permutation(v.cbegin(), v.cend(), 330 | expected.cbegin(), expected.cend()); 331 | } 332 | 333 | DEF_TEST(VectorNoCandidates, VectorEngine) 334 | { 335 | Completer c; 336 | c.AddWord("commit"); 337 | c.AddWord("cherry"); 338 | c.AddWord("cherry-pick"); 339 | 340 | auto v = c.GetCandidates("push"); 341 | return v.size() == 1 && v[0] == "push"; 342 | } 343 | 344 | DEF_TEST(VectorPrefix, VectorEngine) 345 | { 346 | Completer c; 347 | c.AddWord("commit"); 348 | c.AddWord("cherry"); 349 | c.AddWord("cherry-pick"); 350 | 351 | string expected = "cherry"; 352 | auto s = c.CommonPrefix("ch"); 353 | return s == expected; 354 | } 355 | 356 | DEF_TEST(VectorNoPrefix, VectorEngine) 357 | { 358 | Completer c; 359 | c.AddWord("commit"); 360 | c.AddWord("cherry"); 361 | c.AddWord("cherry-pick"); 362 | 363 | auto s = c.CommonPrefix("p"); 364 | return s == "p"; 365 | } 366 | 367 | DEF_TEST(VectorAddWords, VectorEngine) 368 | { 369 | Completer c; 370 | c.AddWords({ "cherry", "commit", "cherry-pick" }); 371 | 372 | vector expected = { "cherry", "cherry-pick" }; 373 | auto v = c.GetCandidates("ch"); 374 | return std::is_permutation(v.cbegin(), v.cend(), 375 | expected.cbegin(), expected.cend()); 376 | } 377 | 378 | DEF_TEST(TernaryTreeCandidates, TernaryTreeEngine) 379 | { 380 | Completer c; 381 | c.AddWord("commit"); 382 | c.AddWord("cherry"); 383 | c.AddWord("cherry-pick"); 384 | 385 | vector expected = { "cherry", "cherry-pick" }; 386 | auto v = c.GetCandidates("ch"); 387 | return std::is_permutation(v.cbegin(), v.cend(), 388 | expected.cbegin(), expected.cend()); 389 | } 390 | 391 | DEF_TEST(TernaryTreeAllCandidates, TernaryTreeEngine) 392 | { 393 | Completer c; 394 | c.AddWord("commit"); 395 | c.AddWord("cherry"); 396 | c.AddWord("cherry-pick"); 397 | 398 | vector expected = { "commit" ,"cherry", "cherry-pick" }; 399 | auto v = c.GetCandidates(""); 400 | return std::is_permutation(v.cbegin(), v.cend(), 401 | expected.cbegin(), expected.cend()); 402 | } 403 | 404 | DEF_TEST(TernaryTreeNoCandidates, TernaryTreeEngine) 405 | { 406 | Completer c; 407 | c.AddWord("commit"); 408 | c.AddWord("cherry"); 409 | c.AddWord("cherry-pick"); 410 | 411 | auto v = c.GetCandidates("push"); 412 | return v.size() == 1 && v[0] == "push"; 413 | } 414 | 415 | DEF_TEST(TernaryTreePrefix, TernaryTreeEngine) 416 | { 417 | Completer c; 418 | c.AddWord("commit"); 419 | c.AddWord("cherry"); 420 | c.AddWord("cherry-pick"); 421 | 422 | string expected = "cherry"; 423 | auto s = c.CommonPrefix("ch"); 424 | return s == expected; 425 | } 426 | 427 | DEF_TEST(TernaryTreeNoPrefix, TernaryTreeEngine) 428 | { 429 | Completer c; 430 | c.AddWord("commit"); 431 | c.AddWord("cherry"); 432 | c.AddWord("cherry-pick"); 433 | 434 | auto s = c.CommonPrefix("p"); 435 | return s == "p"; 436 | } 437 | 438 | DEF_TEST(TernaryTreeAddWords, TernaryTreeEngine) 439 | { 440 | Completer c; 441 | c.AddWords({ "cherry", "commit", "cherry-pick" }); 442 | 443 | vector expected = { "cherry", "cherry-pick" }; 444 | auto v = c.GetCandidates("ch"); 445 | return std::is_permutation(v.cbegin(), v.cend(), 446 | expected.cbegin(), expected.cend()); 447 | } 448 | --------------------------------------------------------------------------------