added stats command

stats is a command that will show you which command you have used the
most, and predicts your next command based on freciency
This commit is contained in:
2025-05-31 14:05:45 +02:00
parent 723939da1b
commit 2e6f7da67c
9 changed files with 114 additions and 27 deletions

View File

@@ -38,7 +38,7 @@ char *builtin_str[] = {
"bookmark", "bookmarks", "goto", "unbookmark", "focus_timer",
"weather", "grep", "grep-text", "ripgrep", "fzf",
"clip", "echo", "theme", "loc", "git_status",
"gg", "ls",
"gg", "ls", "stats",
};
// Array of function pointers to built-in command implementations
@@ -53,6 +53,7 @@ int (*builtin_func[])(char **) = {
&lsh_ripgrep, &lsh_fzf_native, &lsh_clip, &lsh_echo,
&lsh_theme, &lsh_loc, &lsh_git_status, &lsh_gg,
lsh_dir,
&lsh_stats,
};
/**
@@ -1055,3 +1056,70 @@ int lsh_gg(char **args) {
return 1;
}
int lsh_stats(char **args) {
printf("Command Statistics\n");
printf("=================\n\n");
if (frequency_count == 0) {
printf("No command history available\n");
return 1;
}
typedef struct {
char *command;
int count;
double score;
} CommandStat;
CommandStat *stats = malloc(frequency_count * sizeof(CommandStat));
if (!stats)
return 1;
time_t current_time = time(NULL);
for (int i = 0; i < frequency_count; i++) {
stats[i].command = command_frequencies[i].command;
stats[i].count = command_frequencies[i].count;
time_t most_recent = 0;
for (int j = history_size - 1; j >= 0; j--) {
if (strcmp(history_entries[j].command, stats[i].command) == 0) {
most_recent = history_entries[j].timestamp;
break;
}
}
double hours_ago = (current_time - most_recent) / 3600.0;
double recency_weight = hours_ago > 0 ? 1.0 / (1.0 + hours_ago * 0.1) : 1.0;
stats[i].score = stats[i].count * recency_weight;
}
for (int i = 0; i < frequency_count - 1; i++) {
for (int j = i + 1; j < frequency_count; j++) {
if (stats[j].score > stats[i].score) {
CommandStat temp = stats[i];
stats[i] = stats[j];
stats[j] = temp;
}
}
}
printf("Top Commands (by frequency + recency):\n");
printf("Rank Command Count Score\n");
printf("---- -------------------- ----- -----\n");
int display_count = frequency_count < 10 ? frequency_count : 10;
for (int i = 0; i < display_count; i++) {
printf("%-4d %-20s %-5d %.2f\n", i + 1, stats[i].command,
stats[i].count, stats[i].score);
}
printf("\nNext Command Prediction: \n");
if (frequency_count > 0) {
printf("Most likely: %s (score: %.2f)\n", stats[0].command, stats[0].score);
}
free(stats);
return 1;
}

View File

@@ -1,5 +1,5 @@
/**
* builtins.h
* builtins.h
* Declarations for all built-in shell commands
*/
@@ -77,6 +77,7 @@ int lsh_theme(char **args);
int lsh_loc(char **args);
int lsh_git_status(char **args);
int lsh_gg(char **args);
int lsh_stats(char **args);
// Add command to history
void lsh_add_to_history(const char *command);
@@ -84,8 +85,9 @@ void lsh_add_to_history(const char *command);
// Get number of builtin commands
int lsh_num_builtins(void);
// Expose the builtin command strings and function pointers
extern char *builtin_str[];
// Expose the builtin command strings and function pointers
extern char *builtin_str[];
extern int (*builtin_func[])(char **);
/**

Binary file not shown.

View File

@@ -461,7 +461,7 @@ void clear_menu() {
// Clear a generous number of lines to ensure we get everything
// Add extra buffer to handle spacing and indicators
int lines_to_clear = max_menu_lines + 5; // Extra buffer for safety
for (int i = 0; i < lines_to_clear; i++) {
printf("\033[K"); // Clear entire line
if (i < lines_to_clear - 1) {
@@ -476,6 +476,7 @@ void clear_menu() {
menu_start_line = 0;
}
}
// this is a test
/**
* Function to display menu of suggestions
@@ -542,26 +543,27 @@ void display_menu(const char *prompt_buffer, const char *buffer, int position) {
// Store number of menu lines displayed
menu_start_line = show_count;
// Calculate total lines used by the menu (including all spacing and indicators)
// Calculate total lines used by the menu (including all spacing and
// indicators)
int total_lines = 0;
// Initial spacing (2 lines)
total_lines += 2;
// "Above" indicator and its spacing
if (suggestion_count > max_display && start_idx > 0) {
total_lines += 2; // indicator line + spacing line
}
// Menu items
total_lines += show_count;
// "Below" indicator and its spacing
// "Below" indicator and its spacing
if (suggestion_count > max_display && end_idx < suggestion_count) {
total_lines += 2; // spacing line + indicator line
}
// Update max_menu_lines if this is larger
if (total_lines > max_menu_lines) {
max_menu_lines = total_lines;
@@ -779,7 +781,7 @@ char *lsh_read_line(void) {
// Update suggestions after backspace
update_suggestions(buffer, position);
// If we were in menu mode and still have suggestions, stay in menu mode
if (was_in_menu_mode && has_suggestion && suggestion_count > 1) {
menu_mode = 1;
@@ -961,11 +963,13 @@ char *lsh_read_line(void) {
}
}
} else if (c == KEY_SHIFT_TAB) {
// Shift+Tab: cycle backwards in menu mode, or enter menu mode and go to last item
// Shift+Tab: cycle backwards in menu mode, or enter menu mode and go to
// last item
if (menu_mode) {
// Already in menu mode: cycle to previous suggestion
if (suggestion_count > 0) {
suggestion_index = (suggestion_index + suggestion_count - 1) % suggestion_count;
suggestion_index =
(suggestion_index + suggestion_count - 1) % suggestion_count;
// Update full_suggestion with the newly selected suggestion
if (prefix_start > 0) {
@@ -980,13 +984,15 @@ char *lsh_read_line(void) {
char *last_slash = strrchr(path_part, '/');
if (last_slash) {
// Get the directory part up to the last slash (including the slash)
// Get the directory part up to the last slash (including the
// slash)
int dir_part_len = (last_slash - path_part) + 1;
char dir_part[LSH_RL_BUFSIZE] = "";
strncpy(dir_part, path_part, dir_part_len);
dir_part[dir_part_len] = '\0';
// Construct the complete path by keeping the command and directory part
// Construct the complete path by keeping the command and
// directory part
strncpy(full_suggestion, buffer, prefix_start);
full_suggestion[prefix_start] = '\0';
strncat(full_suggestion, dir_part,
@@ -1155,16 +1161,19 @@ char *lsh_read_line(void) {
strncpy(dir_part, path_part, dir_part_len);
dir_part[dir_part_len] = '\0';
// Construct the complete path by keeping the command and directory part
// Construct the complete path by keeping the command and directory
// part
strncpy(temp, buffer, prefix_start);
temp[prefix_start] = '\0';
strncat(temp, dir_part, LSH_RL_BUFSIZE - strlen(temp) - 1);
strncat(temp, suggestions[suggestion_index], LSH_RL_BUFSIZE - strlen(temp) - 1);
strncat(temp, suggestions[suggestion_index],
LSH_RL_BUFSIZE - strlen(temp) - 1);
} else {
// No slash in current argument, normal behavior
strncpy(temp, buffer, prefix_start);
temp[prefix_start] = '\0';
strncat(temp, suggestions[suggestion_index], LSH_RL_BUFSIZE - strlen(temp) - 1);
strncat(temp, suggestions[suggestion_index],
LSH_RL_BUFSIZE - strlen(temp) - 1);
}
strncpy(buffer, temp, bufsize - 1);
@@ -1188,7 +1197,8 @@ char *lsh_read_line(void) {
// Clear suggestions after accepting non-directory
if (suggestions) {
for (int i = 0; i < suggestion_count; i++) {
if (suggestions[i]) free(suggestions[i]);
if (suggestions[i])
free(suggestions[i]);
}
free(suggestions);
suggestions = NULL;
@@ -1261,7 +1271,7 @@ char *lsh_read_line(void) {
// Update with the new character and show suggestions
update_suggestions(buffer, position);
// If we were in menu mode and still have suggestions, stay in menu mode
if (was_in_menu_mode && has_suggestion && suggestion_count > 1) {
menu_mode = 1;

Binary file not shown.

View File

@@ -12,16 +12,17 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <ctype.h>
// Global variables for persistent history
static PersistentHistoryEntry *history_entries = NULL;
static int history_size = 0;
PersistentHistoryEntry *history_entries = NULL;
int history_size = 0;
static int history_capacity = 0;
static int history_position = 0;
// Global variables for frequency tracking
static CommandFrequency *command_frequencies = NULL;
static int frequency_count = 0;
CommandFrequency *command_frequencies = NULL;
int frequency_count = 0;
static int frequency_capacity = 0;
// Paths for history and frequency files

View File

@@ -82,4 +82,10 @@ char **get_matching_history_entries(const char *prefix);
// Free matching entries
void free_matching_entries(char **entries);
// Expose global variables for stats command
extern CommandFrequency *command_frequencies;
extern int frequency_count;
extern PersistentHistoryEntry *history_entries;
extern int history_size;
#endif // PERSISTENT_HISTORY_H

Binary file not shown.

BIN
shell

Binary file not shown.