diff --git a/.clang-format b/.clang-format deleted file mode 100644 index 04fd719..0000000 --- a/.clang-format +++ /dev/null @@ -1,3 +0,0 @@ -Language: Cpp -BasedOnStyle: Google -ColumnLimit: 0 diff --git a/.clang-tidy b/.clang-tidy deleted file mode 100644 index 237092a..0000000 --- a/.clang-tidy +++ /dev/null @@ -1,20 +0,0 @@ -Checks: ' --*, -google-*, --google-runtime-references, --google-readability-avoid-underscore-in-googletest-name, -llvm-include-order, -llvm-namespace-comment, -misc-throw-by-value-catch-by-reference, -modernize*, --modernize-use-trailing-return-type, -readability-container-size-empty, -' - -WarningsAsErrors: '*' - -HeaderFilterRegex: './src/**/*' - -CheckOptions: -- key: google-readability-braces-around-statements.ShortStatementLines - value: '3' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17ec4dd..7a49d0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,97 +1,84 @@ name: CI on: push: { branches: ["0.x"] } pull_request: { branches: ["0.x"] } jobs: commits: name: Commitlint runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 with: fetch-depth: 1000 - name: Lint commits uses: docker://registry.k1.zportal.co.uk/practically-oss/conventional-tools:0.x with: args: conventional-tools commitlint -l1 -f39febd82e236a9c79f5b408e98cbd20410f11e9e luacheck: name: Luacheck runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Install luarocks run: sudo apt update && sudo apt install -y luarocks - name: Install luacheck run: sudo luarocks install luacheck - name: Run luacheck run: luacheck . stylua: name: StyLua runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Run stylua uses: JohnnyMorganz/stylua-action@v2.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} version: latest args: --check . - clang-format: - name: Clang Format - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install Dependencies - run: sudo apt-get update && sudo apt-get install --no-install-recommends -y clang-format findutils - - - name: Run clang format - run: find ./cpp -name "*.cpp" -o -name "*.hpp" | xargs clang-format -Werror --dry-run - cargo-format: name: Cargo Format runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Run cargo format uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check test: name: Build and test runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Install rust toolchain uses: actions-rs/toolchain@v1 with: toolchain: stable - name: Install dependencies run: sudo apt update && sudo apt install -y luajit build-essential - name: Build run: cargo build --release - name: Test run: find lua -name "*_test.lua" | xargs luajit scripts/test.lua diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index a0e4cd8..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,60 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -set(PROJECT_VERSION_NAME "v0.0.1") - -# Split and sanatize the project version so it can be uses as pars and used as -# the project version "v1.1.1" is not a valida version number -string(REPLACE "v" "" PROJECT_VERSION ${PROJECT_VERSION_NAME}) -string(REPLACE "." ";" VERSION_LIST ${PROJECT_VERSION}) -list(GET VERSION_LIST 0 PROJECT_VERSION_MAJOR) -list(GET VERSION_LIST 1 PROJECT_VERSION_MINOR) -list(GET VERSION_LIST 2 PROJECT_VERSION_PATCH) - -project ("Ivy" VERSION ${PROJECT_VERSION}) -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -# Set the build type if its not test -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Release") -endif() - -# Ensure the build type is valid -if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug" AND - NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND - NOT "${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel" AND - NOT "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo") - message(FATAL_ERROR "Unknown build type \"${CMAKE_BUILD_TYPE}\". Allowed values are Debug, Release, RelWithDebInfo, and MinSizeRel.") -endif() - -# detect operating system and host processor -message(STATUS "We are on a ${CMAKE_SYSTEM_NAME} system") -message(STATUS "The host processor is ${CMAKE_HOST_SYSTEM_PROCESSOR}") - -# Place binaries and libraries according to GNU standards. For example -# executables created with `add_executable` will be built into the `bin` -# directory -include(GNUInstallDirs) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}) - -# Set the default compiler flags for GNU -if(CMAKE_CXX_COMPILER_ID MATCHES GNU) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wunreachable-code -Wno-unknown-pragmas -Wno-sign-compare -Wwrite-strings -Wno-unused") - set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3") - set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") -endif() - -find_package(PkgConfig REQUIRED) -find_package(Threads REQUIRED) - -file(GLOB_RECURSE IVY_HEADER "${CMAKE_CURRENT_LIST_DIR}/cpp/*.hpp") -file(GLOB_RECURSE IVY_SOURCE "${CMAKE_CURRENT_LIST_DIR}/cpp/*.cpp") -list(FILTER IVY_SOURCE EXCLUDE REGEX "_test\\.cpp$") -list(FILTER IVY_SOURCE EXCLUDE REGEX "cli\\.cpp$") - -add_library(ivy SHARED ${IVY_SOURCE} ${IVY_HEADER}) -target_link_libraries(ivy Threads::Threads) - -add_executable(ivycli ${IVY_SOURCE} ${IVY_HEADER} ${CMAKE_CURRENT_LIST_DIR}/cpp/cli.cpp) -target_link_libraries(ivycli Threads::Threads) diff --git a/cpp/cli.cpp b/cpp/cli.cpp deleted file mode 100644 index 9bd6d15..0000000 --- a/cpp/cli.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include -#include -#include -#include - -#include "./file_scanner.hpp" -#include "./sorter.hpp" - -int main(int argc, char* argv[]) { - std::vector args; - args.reserve(argc); - // Skip the first argument because that will be the programme name. - for (int i = 1; i < argc; i++) { - args.emplace_back(argv[i]); - } - - if (args.empty()) { - std::cout << "Missing required search term" << std::endl; - return 1; - } - - auto base_dir = std::filesystem::current_path(); - std::string search = args.at(0); - - auto sorter = ivy::Sorter(search); - auto scanner = ivy::FileScanner(base_dir); - - std::regex pattern("([" + search + "])"); - for (ivy::Match const& match : sorter.sort(scanner.scan())) { - std::cout << match.score << " " << std::regex_replace(match.content, pattern, "\033[1m$&\033[0m") << std::endl; - } - - return 0; -} diff --git a/cpp/file_scanner.hpp b/cpp/file_scanner.hpp deleted file mode 100644 index 3fc8209..0000000 --- a/cpp/file_scanner.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace fs = std::filesystem; - -namespace ivy { -class FileScanner { - std::string m_base_dir; - - public: - explicit FileScanner(const std::string base_dir) : m_base_dir(base_dir) {} - - std::vector scan() { - std::vector results; - for (const fs::directory_entry& dir_entry : fs::recursive_directory_iterator(m_base_dir)) { - fs::path path = dir_entry.path(); - - // TODO(ade): sort out some kind of ignore thing. This will be needed - // when we start adding wildcard ignore functionality - if (path.string().find(".git") != std::string::npos) { - continue; - } - - if (dir_entry.is_regular_file()) { - results.emplace_back(fs::relative(path, m_base_dir)); - } - } - - return results; - } -}; -} // namespace ivy diff --git a/cpp/fts_fuzzy_match.hpp b/cpp/fts_fuzzy_match.hpp deleted file mode 100644 index 9b77fe3..0000000 --- a/cpp/fts_fuzzy_match.hpp +++ /dev/null @@ -1,212 +0,0 @@ -// LICENSE -// -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. -// -// VERSION -// 0.2.0 (2017-02-18) Scored matches perform exhaustive search for best score -// 0.1.0 (2016-03-28) Initial release -// -// AUTHOR -// Forrest Smith -// -// NOTES -// Compiling -// You MUST add '#define FTS_FUZZY_MATCH_IMPLEMENTATION' before including this header in ONE source file to create implementation. -// -// fuzzy_match_simple(...) -// Returns true if each character in pattern is found sequentially within str -// -// fuzzy_match(...) -// Returns true if pattern is found AND calculates a score. -// Performs exhaustive search via recursion to find all possible matches and match with highest score. -// Scores values have no intrinsic meaning. Possible score range is not normalized and varies with pattern. -// Recursion is limited internally (default=10) to prevent degenerate cases (pattern="aaaaaa" str="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") -// Uses uint8_t for match indices. Therefore patterns are limited to 256 characters. -// Score system should be tuned for YOUR use case. Words, sentences, file names, or method names all prefer different tuning. - -#ifndef FTS_FUZZY_MATCH_H -#define FTS_FUZZY_MATCH_H - -#include // ::tolower, ::toupper - -#include // uint8_t -#include -#include // memcpy -#include - -// Public interface -namespace fts { -static bool fuzzy_match_simple(char const* pattern, char const* str); -static bool fuzzy_match(char const* pattern, char const* str, int& outScore); -static bool fuzzy_match(char const* pattern, char const* str, int& outScore, uint8_t* matches, int maxMatches); -} // namespace fts - -#ifdef FTS_FUZZY_MATCH_IMPLEMENTATION -namespace fts { - -// Forward declarations for "private" implementation -namespace fuzzy_internal { -static bool fuzzy_match_recursive(const char* pattern, const char* str, int& outScore, const char* strBegin, - uint8_t const* srcMatches, uint8_t* newMatches, int maxMatches, int nextMatch, - int& recursionCount, int recursionLimit); -} - -// Public interface -static bool fuzzy_match_simple(char const* pattern, char const* str) { - while (*pattern != '\0' && *str != '\0') { - if (tolower(*pattern) == tolower(*str)) - ++pattern; - ++str; - } - - return *pattern == '\0' ? true : false; -} - -static bool fuzzy_match(char const* pattern, char const* str, int& outScore) { - uint8_t matches[256]; - return fuzzy_match(pattern, str, outScore, matches, sizeof(matches)); -} - -static bool fuzzy_match(char const* pattern, char const* str, int& outScore, uint8_t* matches, int maxMatches) { - int recursionCount = 0; - int recursionLimit = 10; - - return fuzzy_internal::fuzzy_match_recursive(pattern, str, outScore, str, nullptr, matches, maxMatches, 0, recursionCount, recursionLimit); -} - -// Private implementation -static bool fuzzy_internal::fuzzy_match_recursive(const char* pattern, const char* str, int& outScore, - const char* strBegin, uint8_t const* srcMatches, uint8_t* matches, int maxMatches, - int nextMatch, int& recursionCount, int recursionLimit) { - // Count recursions - ++recursionCount; - if (recursionCount >= recursionLimit) - return false; - - // Detect end of strings - if (*pattern == '\0' || *str == '\0') - return false; - - // Recursion params - bool recursiveMatch = false; - uint8_t bestRecursiveMatches[256]; - int bestRecursiveScore = 0; - - // Loop through pattern and str looking for a match - bool first_match = true; - while (*pattern != '\0' && *str != '\0') { - // Found match. We assume a space in the pattern is match so we can match paths better - if (tolower(*pattern) == tolower(*str) || *pattern == ' ') { - // Supplied matches buffer was too short - if (nextMatch >= maxMatches) { - return false; - } - - // "Copy-on-Write" srcMatches into matches - if (first_match && srcMatches) { - memcpy(matches, srcMatches, nextMatch); - first_match = false; - } - - // Recursive call that "skips" this match - uint8_t recursiveMatches[256]; - int recursiveScore; - if (fuzzy_match_recursive(pattern, str + 1, recursiveScore, strBegin, matches, recursiveMatches, sizeof(recursiveMatches), nextMatch, recursionCount, recursionLimit)) { - // Pick best recursive score - if (!recursiveMatch || recursiveScore > bestRecursiveScore) { - memcpy(bestRecursiveMatches, recursiveMatches, 256); - bestRecursiveScore = recursiveScore; - } - recursiveMatch = true; - } - - // Advance - matches[nextMatch++] = (uint8_t)(str - strBegin); - ++pattern; - } - ++str; - } - - // Determine if full pattern was matched - bool matched = *pattern == '\0' ? true : false; - - // Calculate score - if (matched) { - const int sequential_bonus = 15; // bonus for adjacent matches - const int separator_bonus = 30; // bonus if match occurs after a separator - const int camel_bonus = 30; // bonus if match is uppercase and prev is lower - const int first_letter_bonus = 15; // bonus if the first letter is matched - - const int leading_letter_penalty = -5; // penalty applied for every letter in str before the first match - const int max_leading_letter_penalty = -15; // maximum penalty for leading letters - const int unmatched_letter_penalty = -1; // penalty for every letter that doesn't matter - - // Iterate str to end - while (*str != '\0') - ++str; - - // Initialize score - outScore = 100; - - // Apply leading letter penalty - int penalty = leading_letter_penalty * matches[0]; - if (penalty < max_leading_letter_penalty) - penalty = max_leading_letter_penalty; - outScore += penalty; - - // Apply unmatched penalty - int unmatched = (int)(str - strBegin) - nextMatch; - outScore += unmatched_letter_penalty * unmatched; - - // Apply ordering bonuses - for (int i = 0; i < nextMatch; ++i) { - uint8_t currIdx = matches[i]; - - if (i > 0) { - uint8_t prevIdx = matches[i - 1]; - - // Sequential - if (currIdx == (prevIdx + 1)) - outScore += sequential_bonus; - } - - // Check for bonuses based on neighbor character value - if (currIdx > 0) { - // Camel case - char neighbor = strBegin[currIdx - 1]; - char curr = strBegin[currIdx]; - if (::islower(neighbor) && ::isupper(curr)) - outScore += camel_bonus; - - // Separator - bool neighborSeparator = neighbor == '_' || neighbor == ' ' || neighbor == '/' || neighbor == '-'; - if (neighborSeparator) - outScore += separator_bonus; - } else { - // First letter - outScore += first_letter_bonus; - } - } - } - - // Return best result - if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) { - // Recursive score is better than "this" - memcpy(matches, bestRecursiveMatches, maxMatches); - outScore = bestRecursiveScore; - return true; - } else if (matched) { - // "this" score is better than recursive - return true; - } else { - // no match - return false; - } -} -} // namespace fts - -#endif // FTS_FUZZY_MATCH_IMPLEMENTATION - -#endif // FTS_FUZZY_MATCH_H diff --git a/cpp/fuzzy_match.cpp b/cpp/fuzzy_match.cpp deleted file mode 100644 index 6a5d15d..0000000 --- a/cpp/fuzzy_match.cpp +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2017-2018 ccls Authors -// SPDX-License-Identifier: Apache-2.0 - -// https://github.com/MaskRay/ccls/blob/master/src/fuzzy_match.cc - -#include "fuzzy_match.hpp" - -#include -#include - -#include -#include - -namespace ivy { -namespace { -enum CharClass { Other, - Lower, - Upper }; -enum CharRole { None, - Tail, - Head }; - -CharClass getCharClass(int c) { - if (islower(c)) - return Lower; - if (isupper(c)) - return Upper; - return Other; -} - -void calculateRoles(std::string_view s, int roles[], int *class_set) { - if (s.empty()) { - *class_set = 0; - return; - } - CharClass pre = Other, cur = getCharClass(s[0]), suc; - *class_set = 1 << cur; - auto fn = [&]() { - if (cur == Other) - return None; - // U(U)L is Head while U(U)U is Tail - return pre == Other || (cur == Upper && (pre == Lower || suc == Lower)) - ? Head - : Tail; - }; - for (size_t i = 0; i < s.size() - 1; i++) { - suc = getCharClass(s[i + 1]); - *class_set |= 1 << suc; - roles[i] = fn(); - pre = cur; - cur = suc; - } - roles[s.size() - 1] = fn(); -} -} // namespace - -int FuzzyMatcher::missScore(int j, bool last) { - int s = -3; - if (last) - s -= 10; - if (text_role[j] == Head) - s -= 10; - return s; -} - -int FuzzyMatcher::matchScore(int i, int j, bool last) { - int s = 0; - // Case matching. - if (pat[i] == text[j]) { - s++; - // pat contains uppercase letters or prefix matching. - if ((pat_set & 1 << Upper) || i == j) - s++; - } - if (pat_role[i] == Head) { - if (text_role[j] == Head) - s += 30; - else if (text_role[j] == Tail) - s -= 10; - } - // Matching a tail while previous char wasn't matched. - if (text_role[j] == Tail && i && !last) - s -= 30; - // First char of pat matches a tail. - if (i == 0 && text_role[j] == Tail) - s -= 40; - return s; -} - -FuzzyMatcher::FuzzyMatcher(std::string_view pattern, int sensitivity) { - calculateRoles(pattern, pat_role, &pat_set); - if (sensitivity == 1) - sensitivity = pat_set & 1 << Upper ? 2 : 0; - case_sensitivity = sensitivity; - size_t n = 0; - for (size_t i = 0; i < pattern.size(); i++) - if (pattern[i] != ' ') { - pat += pattern[i]; - low_pat[n] = (char)::tolower(pattern[i]); - pat_role[n] = pat_role[i]; - n++; - } -} - -int FuzzyMatcher::match(std::string_view text, bool strict) { - if (pat.empty() != text.empty()) - return kMinScore; - int n = int(text.size()); - if (n > kMaxText) - return kMinScore + 1; - this->text = text; - for (int i = 0; i < n; i++) - low_text[i] = (char)::tolower(text[i]); - calculateRoles(text, text_role, &text_set); - if (strict && n && !!pat_role[0] != !!text_role[0]) - return kMinScore; - dp[0][0][0] = dp[0][0][1] = 0; - for (int j = 0; j < n; j++) { - dp[0][j + 1][0] = dp[0][j][0] + missScore(j, false); - dp[0][j + 1][1] = kMinScore * 2; - } - for (int i = 0; i < int(pat.size()); i++) { - int(*pre)[2] = dp[i & 1]; - int(*cur)[2] = dp[(i + 1) & 1]; - cur[i][0] = cur[i][1] = kMinScore; - for (int j = i; j < n; j++) { - cur[j + 1][0] = std::max(cur[j][0] + missScore(j, false), - cur[j][1] + missScore(j, true)); - // For the first char of pattern, apply extra restriction to filter bad - // candidates (e.g. |int| in |PRINT|) - cur[j + 1][1] = (case_sensitivity ? pat[i] == text[j] - : low_pat[i] == low_text[j] && - (i || text_role[j] != Tail || - pat[i] == text[j])) - ? std::max(pre[j][0] + matchScore(i, j, false), - pre[j][1] + matchScore(i, j, true)) - : kMinScore * 2; - } - } - - // Enumerate the end position of the match in str. Each removed trailing - // character has a penulty. - int ret = kMinScore; - for (int j = pat.size(); j <= n; j++) - ret = std::max(ret, dp[pat.size() & 1][j][1] - 2 * (n - j)); - return ret; -} -} // namespace ivy - -#if 0 -TEST_SUITE("fuzzy_match") { - bool Ranks(std::string_view pat, std::vector texts) { - FuzzyMatcher fuzzy(pat, 0); - std::vector scores; - for (auto text : texts) - scores.push_back(fuzzy.Match(text)); - bool ret = true; - for (size_t i = 0; i < texts.size() - 1; i++) - if (scores[i] < scores[i + 1]) { - ret = false; - break; - } - if (!ret) { - for (size_t i = 0; i < texts.size(); i++) - printf("%s %d ", texts[i], scores[i]); - puts(""); - } - return ret; - } - - TEST_CASE("test") { - FuzzyMatcher fuzzy("", 0); - CHECK(fuzzy.Match("") == 0); - CHECK(fuzzy.Match("aaa") < 0); - - // case - CHECK(Ranks("monad", {"monad", "Monad", "mONAD"})); - // initials - CHECK(Ranks("ab", {"ab", "aoo_boo", "acb"})); - CHECK(Ranks("CC", {"CamelCase", "camelCase", "camelcase"})); - CHECK(Ranks("cC", {"camelCase", "CamelCase", "camelcase"})); - CHECK(Ranks("c c", {"camelCase", "camel case", "CamelCase", "camelcase", - "camel ace"})); - CHECK(Ranks("Da.Te", - {"Data.Text", "Data.Text.Lazy", "Data.Aeson.Encoding.text"})); - CHECK(Ranks("foo bar.h", {"foo/bar.h", "foobar.h"})); - // prefix - CHECK(Ranks("is", {"isIEEE", "inSuf"})); - // shorter - CHECK(Ranks("ma", {"map", "many", "maximum"})); - CHECK(Ranks("print", {"printf", "sprintf"})); - // score(PRINT) = kMinScore - CHECK(Ranks("ast", {"ast", "AST", "INT_FAST16_MAX"})); - // score(PRINT) > kMinScore - CHECK(Ranks("Int", {"int", "INT", "PRINT"})); - } -} -#endif diff --git a/cpp/fuzzy_match.hpp b/cpp/fuzzy_match.hpp deleted file mode 100644 index 9a02670..0000000 --- a/cpp/fuzzy_match.hpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2017-2018 ccls Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include - -namespace ivy { -class FuzzyMatcher { - public: - constexpr static int kMaxPat = 100; - constexpr static int kMaxText = 200; - // Negative but far from INT_MIN so that intermediate results are hard to - // overflow. - constexpr static int kMinScore = INT_MIN / 4; - - // 0: case-insensitive - // 1: case-folded, i.e. insensitive if no input character is uppercase. - // 2: case-sensitive - FuzzyMatcher(std::string_view pattern, int case_sensitivity); - int match(std::string_view text, bool strict); - - private: - int case_sensitivity; - std::string pat; - std::string_view text; - int pat_set, text_set; - char low_pat[kMaxPat], low_text[kMaxText]; - int pat_role[kMaxPat], text_role[kMaxText]; - int dp[2][kMaxText + 1][2]; - - int matchScore(int i, int j, bool last); - int missScore(int j, bool last); -}; -} // namespace ivy diff --git a/cpp/lib.cpp b/cpp/lib.cpp deleted file mode 100644 index b97f478..0000000 --- a/cpp/lib.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include -#include -#include - -#define FTS_FUZZY_MATCH_IMPLEMENTATION -#include "./file_scanner.hpp" -#include "./fts_fuzzy_match.hpp" -#include "./match.hpp" -#include "./sorter.hpp" - -namespace ivy { -static std::map> file_cache; -}; // namespace ivy - -extern "C" void ivy_init(const char* dir) { - auto scanner = ivy::FileScanner(dir); - ivy::file_cache[std::string(dir)] = scanner.scan(); -} - -extern "C" int ivy_match(const char* pattern, const char* text) { - int score = 0; - fts::fuzzy_match(pattern, text, score); - - return score; -} - -extern "C" char* ivy_files(const char* search, const char* base_dir) { - if (!ivy::file_cache.count(base_dir)) { - auto scanner = ivy::FileScanner(base_dir); - ivy::file_cache[std::string(base_dir)] = scanner.scan(); - } - - auto sorter = ivy::Sorter(search); - - // TODO(ade): Sort out how this memory is freed. I am assuming its in lua - // land via ffi - auto* s = new std::string(); - for (ivy::Match const& match : sorter.sort(ivy::file_cache.at(base_dir))) { - s->append(match.content + "\n"); - } - - return s->data(); -} diff --git a/cpp/match.hpp b/cpp/match.hpp deleted file mode 100644 index 2ca6d1c..0000000 --- a/cpp/match.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -namespace ivy { - -struct Match { - int score; - std::string content; -}; - -static bool sort_match(const Match& a, const Match& b) { return a.score < b.score; } - -} // namespace ivy diff --git a/cpp/sorter.hpp b/cpp/sorter.hpp deleted file mode 100644 index 02fa900..0000000 --- a/cpp/sorter.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#define FTS_FUZZY_MATCH_IMPLEMENTATION -#include "./fts_fuzzy_match.hpp" -#include "./match.hpp" -#include "./thread_pool.hpp" - -namespace ivy { - -class Sorter { - ivy::ThreadPool m_thread_pool; - - std::string m_term; - - std::mutex m_matches_lock; - std::vector m_matches; - - inline void add_entry(const std::string& file) { - int score = 0; - fts::fuzzy_match(m_term.c_str(), file.c_str(), score); - - if (score > 50) { - std::unique_lock lock(m_matches_lock); - m_matches.emplace_back(Match{score, std::move(file)}); - } - } - - public: - explicit Sorter(std::string_view term) : m_term(term) {} - ~Sorter() { m_thread_pool.shutdown(); } - - inline std::vector sort(const std::vector& list) { - for (const std::string& item : list) { - m_thread_pool.push([&item, this]() { add_entry(item); }); - } - - while (!m_thread_pool.empty()) { - // Wait for all of the jobs to be finished - } - - std::sort(m_matches.begin(), m_matches.end(), sort_match); - return m_matches; - } -}; - -} // namespace ivy diff --git a/cpp/thread_pool.cpp b/cpp/thread_pool.cpp deleted file mode 100644 index 710bf13..0000000 --- a/cpp/thread_pool.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2021 Practically.io All rights reserved -// -// Use of this source is governed by a BSD-style -// licence that can be found in the LICENCE file or at -// https://www.practically.io/copyright/ - -#include "thread_pool.hpp" - -namespace ivy { -void ThreadPool::run_job() { - std::function job; - while (true) { - { - std::unique_lock lock(m_queue_lock); - m_condition.wait(lock, [&]() { return !m_queue.empty() || m_stop; }); - if (m_queue.empty()) { - return; - } - - job = m_queue.front(); - m_queue.pop(); - } - - job(); - - { - // Only decrement the job count when the job has finished running. - std::unique_lock lock(m_count_lock); - m_job_count--; - } - } -} - -void ThreadPool::create_threads(unsigned int thread_count) { - for (int i = 0; i < thread_count; i++) { - m_threads.emplace_back(std::thread([this] { run_job(); })); - } -} - -void ThreadPool::push(std::function job) { - { - { - std::unique_lock lock(m_count_lock); - m_job_count++; - } - - std::unique_lock lock(m_queue_lock); - m_queue.push(job); - } - - m_condition.notify_one(); -} - -bool ThreadPool::empty() { - std::unique_lock lock(m_count_lock); - return m_job_count == 0; -} - -void ThreadPool::shutdown() { - { - std::unique_lock lock(m_queue_lock); - m_stop = true; - } - - m_condition.notify_all(); - for (auto &thread : m_threads) { - thread.join(); - } -} -} // namespace ivy diff --git a/cpp/thread_pool.hpp b/cpp/thread_pool.hpp deleted file mode 100644 index 21c33fe..0000000 --- a/cpp/thread_pool.hpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2021 Practically.io All rights reserved -// -// Use of this source is governed by a BSD-style -// licence that can be found in the LICENCE file or at -// https://www.practically.io/copyright/ -#pragma once - -#include -#include -#include -#include - -namespace ivy { -// Basic thread pool implementation to run callbacks distributed across -// specified number of threads -// -// Example: -// -// ivy::ThreadPool thread_pool; -// for (int i = 0; i < 10; i++) { -// thread_pool.push([i]() { -// std::cout << "The number is " << i << std::endl; -// }); -// } -// -// thread_pool.shutdown(); -// -class ThreadPool { - bool m_stop = false; - // Need to track the number of jobs that need to be processed separately - // because we cant rely on the queue length to check if pool has finished all - // the jobs. It dose not take into account the jobs that have already been - // picked up by a thread. - int m_job_count = 0; - std::mutex m_queue_lock; - - std::queue> m_queue; - std::mutex m_count_lock; - - std::vector m_threads; - std::condition_variable m_condition; - - void run_job(); - void create_threads(unsigned int thread_count); - - public: - // Create a new thread pool with the maximum number of threads you can have on - // the current machine - ThreadPool() { create_threads(std::thread::hardware_concurrency()); } - // Create a thread pool that will use the specified number of threads - explicit ThreadPool(unsigned int thread_count) { - create_threads(thread_count); - } - // Push a call back function into the queue that will be run on the thread - // pool as some time. - void push(std::function); - // Tests to see if there is any jobs that still need to be processed by the - // queue - bool empty(); - // Shuts down the thread pool and waits for the queue to be empty. This must - // be called when all of the jobs have been pushed into the queue. This is a - // blocking operation and will not exit until the queue is empty and all of - // the pushed jobs have been handled. - void shutdown(); -}; -} // namespace ivy