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:
70
builtins.c
70
builtins.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 **);
|
||||
|
||||
/**
|
||||
|
||||
BIN
builtins.o
BIN
builtins.o
Binary file not shown.
@@ -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;
|
||||
|
||||
BIN
line_reader.o
BIN
line_reader.o
Binary file not shown.
@@ -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
|
||||
|
||||
@@ -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.
Reference in New Issue
Block a user