Browse Source

Fixed sort and added potentially better matching

* I made a mistake with my quicksortReverse so it wasn't sorting
  things at all. It's fixed now
* Added fuzzy_match_better() in hopes that I could write a simpler
  fuzzy match scoring algorithm with better results. It's not quite
  done yet so I haven't done a comparision
* Added some debugging stuff
master
Macoy Madson 4 years ago
parent
commit
79a8ed6f9c
  1. 59
      fuzzy.c
  2. 3
      fuzzy.h
  3. 99
      macoyFuzzy.c
  4. 1
      macoyFuzzy.el
  5. 58
      utils.c
  6. 19
      utils.h

59
fuzzy.c

@ -187,3 +187,62 @@ static bool fuzzy_match_recursive(const char* pattern, const char* str, int* out
return false;
}
}
bool fuzzy_match_better(char const* pattern, char const* str, int* outScore)
{
const int initialScore = 50;
const int firstCharacterValue = 10;
const int acronymCharacterValue = 8;
// First, make sure we have all characters in pattern
{
char const* patternIter = pattern;
char const* strIter = str;
while (*patternIter != '\0' && *strIter != '\0')
{
if (tolower(*patternIter) == tolower(*strIter))
++patternIter;
++strIter;
}
if (*patternIter != '\0')
return false;
}
// Score acronyms. We'll record the string length here too (this code should be fast)
int acronymScore = 0;
int strLength = 0;
{
char const* patternIter = pattern;
for (char const* strIter = str; *strIter != '\0' && *patternIter != '\0'; ++strIter)
{
++strLength;
if (tolower(*patternIter) == tolower(*strIter))
{
// First character matches get bonus
if (strIter == str)
acronymScore += firstCharacterValue;
// Handle underscore_delimited, camelCase, lisp-style, and file/dir/paths
else if (*(strIter - 1) == '_' || *(strIter - 1) == '/' || *(strIter - 1) == '-' ||
(isupper(*patternIter) && islower(*(strIter - 1))))
acronymScore += acronymCharacterValue;
patternIter++;
}
}
}
// Score consecutive matches
int consecutiveMatchesScore = 0;
{
// We're going to do this later
}
// Total score. Penalize longer strings
*outScore = (initialScore - strLength) + (acronymScore + consecutiveMatchesScore);
return true;
}

3
fuzzy.h

@ -38,3 +38,6 @@ bool fuzzy_match_simple(char const* pattern, char const* str);
bool fuzzy_match(char const* pattern, char const* str, int* outScore);
bool fuzzy_match_with_matches(char const* pattern, char const* str, int* outScore,
unsigned char* matches, int maxMatches);
// Macoy's version
bool fuzzy_match_better(char const* pattern, char const* str, int* outScore);

99
macoyFuzzy.c

@ -1,5 +1,12 @@
// Created by Macoy Madson
// GPL V3
// https://github.com/makuto/emacs-fuzzy-module
#include <emacs-module.h>
// remove (for printf)
#include <stdio.h>
#include <stdlib.h>
#include "fuzzy.h"
@ -7,6 +14,11 @@
int plugin_is_GPL_compatible;
// Uncomment for printf debug output
//#define MACOY_FUZZY_DEBUG
#define FUZZY_MATCH(query, string, outScore) fuzzy_match_better(query, string, outScore)
// Note that for now this will affect the quality of the results if e.g. the best result is actually
// match #2049. I'll have to make this good eventually
#define MAX_MATCHES 2048
@ -17,66 +29,6 @@ typedef struct MacoyFuzzyMatch
int score;
} MacoyFuzzyMatch;
// Return the value of the given element
typedef int (*quicksort_GetValueFunc)(void* value);
// Sorts in place (modifies array)
// Modified from https://rosettacode.org/wiki/Sorting_algorithms/Quicksort#C (GNU FDL license)
void quicksort(void** array, int length, quicksort_GetValueFunc getValue)
{
if (length < 2)
return;
int pivot = getValue(array[length / 2]);
int i, j;
for (i = 0, j = length - 1;; i++, j--)
{
while (getValue(array[i]) < pivot)
i++;
while (getValue(array[j]) > pivot)
j--;
if (i >= j)
break;
void* temp = array[i];
array[i] = array[j];
array[j] = temp;
}
quicksort(array, i, getValue);
quicksort(array + i, length - i, getValue);
}
// Sorts in place (modifies array)
// Modified from https://rosettacode.org/wiki/Sorting_algorithms/Quicksort#C (GNU FDL license)
void quicksortReverse(void** array, int length, quicksort_GetValueFunc getValue)
{
if (length < 2)
return;
int pivot = getValue(array[length / 2]);
int i, j;
for (i = 0, j = length - 1;; i++, j--)
{
while (getValue(array[i]) > pivot)
i++;
while (getValue(array[j]) < pivot)
j--;
if (i >= j)
break;
void* temp = array[i];
array[i] = array[j];
array[j] = temp;
}
quicksort(array, i, getValue);
quicksort(array + i, length - i, getValue);
}
int getFuzzyMatchValue(void* match)
{
return ((MacoyFuzzyMatch*)match)->score;
@ -127,7 +79,7 @@ static emacs_value FmacoyFuzzyFilterVector_fun(emacs_env* env, ptrdiff_t nargs,
copy_string_contents(env, currentString, &stringToCheckBuffer, &stringToCheckBufferSize);
int score = 0;
bool isMatch = fuzzy_match(queryBuffer, stringToCheckBuffer, &score);
bool isMatch = FUZZY_MATCH(queryBuffer, stringToCheckBuffer, &score);
free(stringToCheckBuffer);
@ -144,22 +96,35 @@ static emacs_value FmacoyFuzzyFilterVector_fun(emacs_env* env, ptrdiff_t nargs,
}
// Reached max number of matches
else
break;
break;
}
free(queryBuffer);
if (numMatches)
{
MacoyFuzzyMatch** sortedMatches = sortFuzzyMatches(matches, numMatches);
#ifdef MACOY_FUZZY_DEBUG
printf("\nQuery: %s\n", queryBuffer);
for (int i = 0; i < numMatches; ++i)
{
char* stringBuffer = NULL;
size_t stringBufferSize = 0;
copy_string_contents(env, sortedMatches[i]->string, &stringBuffer, &stringBufferSize);
printf("%s score: %d\n", stringBuffer, sortedMatches[i]->score);
free(stringBuffer);
}
#endif
emacs_value matchesList = makeListFromFuzzyMatches(env, sortedMatches, numMatches);
free(sortedMatches);
free(queryBuffer);
return matchesList;
}
else
{
emacs_value emptyList[] = {};
free(queryBuffer);
return env->funcall(env, env->intern(env, "list"), 0, emptyList);
}
}
@ -176,7 +141,7 @@ static emacs_value FmacoyFuzzyScore_fun(emacs_env* env, ptrdiff_t nargs, emacs_v
copy_string_contents(env, args[1], &stringToCheckBuffer, &stringToCheckBufferSize);
int outScore = 0;
fuzzy_match(queryBuffer, stringToCheckBuffer, &outScore);
FUZZY_MATCH(queryBuffer, stringToCheckBuffer, &outScore);
free(queryBuffer);
free(stringToCheckBuffer);
@ -197,7 +162,3 @@ int emacs_module_init(struct emacs_runtime* ert)
provide(env, "macoy-fuzzy");
return 0;
}
/* Integration
Follow flx-ido defadvice for ido to replace (esp ido-set-matches-1)
*/

1
macoyFuzzy.el

@ -60,6 +60,7 @@
:group 'ido
:global t)
;; TODO: Sort list by last used? (duplicate whatever behavior the normal stuff does)
(defun macoy-filter-list-fuzzy-ido (query items)
(if (zerop (length query))
items

58
utils.c

@ -54,3 +54,61 @@ void provide(emacs_env* env, const char* feature)
env->funcall(env, Qprovide, 1, args);
}
// Sorts in place (modifies array)
// Modified from https://rosettacode.org/wiki/Sorting_algorithms/Quicksort#C (GNU FDL license)
void quicksort(void** array, int length, quicksort_GetValueFunc getValue)
{
if (length < 2)
return;
int pivot = getValue(array[length / 2]);
int i, j;
for (i = 0, j = length - 1;; i++, j--)
{
while (getValue(array[i]) < pivot)
i++;
while (getValue(array[j]) > pivot)
j--;
if (i >= j)
break;
void* temp = array[i];
array[i] = array[j];
array[j] = temp;
}
quicksort(array, i, getValue);
quicksort(array + i, length - i, getValue);
}
// Sorts in place (modifies array)
// Modified from https://rosettacode.org/wiki/Sorting_algorithms/Quicksort#C (GNU FDL license)
void quicksortReverse(void** array, int length, quicksort_GetValueFunc getValue)
{
if (length < 2)
return;
int pivot = getValue(array[length / 2]);
int i, j;
for (i = 0, j = length - 1;; i++, j--)
{
while (getValue(array[i]) > pivot)
i++;
while (getValue(array[j]) < pivot)
j--;
if (i >= j)
break;
void* temp = array[i];
array[i] = array[j];
array[j] = temp;
}
quicksortReverse(array, i, getValue);
quicksortReverse(array + i, length - i, getValue);
}

19
utils.h

@ -4,6 +4,10 @@
#include <emacs-module.h>
//
// Emacs helpers
//
// Free() the buffer once you're done with it
bool copy_string_contents(emacs_env* env, emacs_value value, char** buffer, size_t* size);
@ -12,3 +16,18 @@ void bind_function(emacs_env* env, const char* name, emacs_value Sfun);
/* Provide FEATURE to Emacs. */
void provide(emacs_env* env, const char* feature);
//
// Quicksort
//
// Return the value of the given element
typedef int (*quicksort_GetValueFunc)(void* value);
// Sorts in place (modifies array)
// Modified from https://rosettacode.org/wiki/Sorting_algorithms/Quicksort#C (GNU FDL license)
void quicksort(void** array, int length, quicksort_GetValueFunc getValue);
// Sorts in place (modifies array)
// Modified from https://rosettacode.org/wiki/Sorting_algorithms/Quicksort#C (GNU FDL license)
void quicksortReverse(void** array, int length, quicksort_GetValueFunc getValue);

Loading…
Cancel
Save