hachem.wtf
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-02 14:51:55 +0200
committerhachem <im@hachem.wtf>2026-09-02 14:51:55 +0200
commitfc119bb5569a34ebf11ede611371a2afc97d6f66 (patch)
tree7263ced4b2a041ae6b7248423ca1954d90611d62
parente12f6224e317dd666e3a2721f57b3c5b7517d304 (diff)
feat: sort pages with relevancy
-rw-r--r--jafm.c191
1 files changed, 140 insertions, 51 deletions
diff --git a/jafm.c b/jafm.c
index abfb044..18e634b 100644
--- a/jafm.c
+++ b/jafm.c
@@ -14,18 +14,16 @@
#define LINE_SIZE 8192
#define MAX_VISIBLE 12
-enum Rank
-{
- RANK_FILENAME,
- RANK_NAME,
- RANK_BODY,
- RANK_NONE
-};
+#define WEIGHT_FILENAME 1000000
+#define WEIGHT_NAME 5000
+#define WEIGHT_DEFINITION 2000
+#define WEIGHT_SYNOPSIS 50
+#define WEIGHT_BODY 1
struct Match
{
char* path;
- enum Rank rank;
+ long score;
};
struct MatchList
@@ -35,7 +33,7 @@ struct MatchList
size_t capacity;
};
-static bool match_list_add(struct MatchList* matches, const char* path, enum Rank rank)
+static bool match_list_add(struct MatchList* matches, const char* path, long score)
{
if (matches->count == matches->capacity)
{
@@ -54,7 +52,7 @@ static bool match_list_add(struct MatchList* matches, const char* path, enum Ran
return false;
matches->items[matches->count].path = copy;
- matches->items[matches->count].rank = rank;
+ matches->items[matches->count].score = score;
matches->count++;
return true;
}
@@ -72,8 +70,8 @@ static int compare_matches(const void* left, const void* right)
const struct Match* first = left;
const struct Match* second = right;
- if (first->rank != second->rank)
- return first->rank < second->rank ? -1 : 1;
+ if (first->score != second->score)
+ return first->score > second->score ? -1 : 1;
return strcmp(first->path, second->path);
}
@@ -94,71 +92,126 @@ static bool is_section_header(const char* line)
return strncmp(line, ".SH", 3) == 0 || strncmp(line, ".Sh", 3) == 0;
}
-static void classify_line(const char* line, const char* query, bool* in_name,
- bool* found_name, bool* found_body)
+static bool is_definition_line(const char* line)
{
- if (is_section_header(line))
- *in_name = strstr(line, "NAME") != NULL;
+ if (strchr(line, '{') == NULL)
+ return false;
- if (strstr(line, query) == NULL)
- return;
+ return strstr(line, "struct") != NULL || strstr(line, "union") != NULL
+ || strstr(line, "enum") != NULL || strstr(line, "typedef") != NULL;
+}
- *found_body = true;
- if (*in_name || strncmp(line, ".Nm", 3) == 0)
- *found_name = true;
+static long count_occurrences(const char* line, const char* query)
+{
+ size_t query_length = strlen(query);
+ if (query_length == 0)
+ return 0;
+
+ long count = 0;
+ for (const char* cursor = strstr(line, query);
+ cursor != NULL;
+ cursor = strstr(cursor + query_length, query))
+ count++;
+
+ return count;
}
-static enum Rank rank_from(bool found_name, bool found_body)
+struct Scan
+{
+ bool in_name;
+ bool in_synopsis;
+ long name_hits;
+ long definition_hits;
+ long synopsis_hits;
+ long body_hits;
+};
+
+static void scan_line(struct Scan* scan, const char* line, const char* query)
{
- if (found_name)
- return RANK_NAME;
+ if (is_section_header(line))
+ {
+ scan->in_name = strstr(line, "NAME") != NULL;
+ scan->in_synopsis = strstr(line, "SYNOPSIS") != NULL;
+ return;
+ }
+
+ long hits = count_occurrences(line, query);
+ if (hits == 0)
+ return;
- if (found_body)
- return RANK_BODY;
+ if (scan->in_name || strncmp(line, ".Nm", 3) == 0)
+ scan->name_hits += hits;
+ else if (is_definition_line(line))
+ scan->definition_hits += hits;
+ else if (scan->in_synopsis)
+ scan->synopsis_hits += hits;
+ else
+ scan->body_hits += hits;
+}
- return RANK_NONE;
+static long scan_score(const struct Scan* scan)
+{
+ return scan->name_hits * WEIGHT_NAME + scan->definition_hits * WEIGHT_DEFINITION
+ + scan->synopsis_hits * WEIGHT_SYNOPSIS + scan->body_hits * WEIGHT_BODY;
}
-static enum Rank plain_page_rank(const char* path, const char* query)
+static long plain_page_score(const char* path, const char* query)
{
FILE* page = fopen(path, "r");
if (page == NULL)
- return RANK_NONE;
+ return 0;
+ struct Scan scan = {0};
char line[LINE_SIZE];
- bool in_name = false;
- bool found_name = false;
- bool found_body = false;
while (fgets(line, sizeof(line), page) != NULL)
- classify_line(line, query, &in_name, &found_name, &found_body);
+ scan_line(&scan, line, query);
(void) fclose(page);
- return rank_from(found_name, found_body);
+ return scan_score(&scan);
}
-static enum Rank gzip_page_rank(const char* path, const char* query)
+static long gzip_page_score(const char* path, const char* query)
{
gzFile page = gzopen(path, "r");
if (page == NULL)
- return RANK_NONE;
+ return 0;
+ struct Scan scan = {0};
char line[LINE_SIZE];
- bool in_name = false;
- bool found_name = false;
- bool found_body = false;
while (gzgets(page, line, LINE_SIZE) != NULL)
- classify_line(line, query, &in_name, &found_name, &found_body);
+ scan_line(&scan, line, query);
(void) gzclose(page);
- return rank_from(found_name, found_body);
+ return scan_score(&scan);
}
-static enum Rank page_rank(const char* path, const char* query)
+static long page_score(const char* path, const char* query)
{
if (ends_with(path, ".gz"))
- return gzip_page_rank(path, query);
+ return gzip_page_score(path, query);
+
+ return plain_page_score(path, query);
+}
+
+static long section_weight(const char* section)
+{
+ const char* name = strrchr(section, '/');
+ name = name != NULL ? name + 1 : section;
+ if (strncmp(name, "man", 3) == 0)
+ name += 3;
- return plain_page_rank(path, query);
+ switch (name[0])
+ {
+ case '0':
+ case '2':
+ case '4':
+ case '5':
+ case '7':
+ case '9':
+ return 8;
+ default:
+ return 1;
+ }
}
static bool filename_matches(const char* name, const char* query)
@@ -399,14 +452,12 @@ static void list_pages(const char* section, const char* query, struct MatchList*
char path[4096];
(void) snprintf(path, sizeof(path), "%s/%s", section, entry->d_name);
+ long score = page_score(path, query) * section_weight(section);
if (filename_matches(entry->d_name, query))
- (void) match_list_add(matches, path, RANK_FILENAME);
- else
- {
- enum Rank rank = page_rank(path, query);
- if (rank != RANK_NONE)
- (void) match_list_add(matches, path, rank);
- }
+ score += WEIGHT_FILENAME;
+
+ if (score > 0)
+ (void) match_list_add(matches, path, score);
}
(void) closedir(section_stream);
@@ -431,6 +482,43 @@ static void list_sections(const char* directory, const char* query, struct Match
(void) closedir(directory_stream);
}
+static void dedupe_matches(struct MatchList* matches)
+{
+ if (matches->count == 0)
+ return;
+
+ char (*labels)[256] = malloc(matches->count * sizeof(*labels));
+ if (labels == NULL)
+ return;
+
+ size_t write = 0;
+ for (size_t read = 0; read < matches->count; read++)
+ {
+ char label[256];
+ page_label(matches->items[read].path, label, sizeof(label));
+
+ bool seen = false;
+ for (size_t index = 0; index < write; index++)
+ if (strcmp(labels[index], label) == 0)
+ {
+ seen = true;
+ break;
+ }
+
+ if (seen)
+ free(matches->items[read].path);
+ else
+ {
+ (void) snprintf(labels[write], sizeof(labels[write]), "%s", label);
+ matches->items[write] = matches->items[read];
+ write++;
+ }
+ }
+
+ free(labels);
+ matches->count = write;
+}
+
static int open_page(const char* path)
{
char work[4096];
@@ -512,6 +600,7 @@ int main(int argc, char** argv)
}
qsort(matches.items, matches.count, sizeof(struct Match), compare_matches);
+ dedupe_matches(&matches);
size_t selected = 0;
bool has_selection;