Merging favorites branch - command favorites are comming ;)

This commit is contained in:
Martin Dvorak 2014-04-13 09:56:32 +02:00
commit f995ecdc1d
9 changed files with 395 additions and 48 deletions

View file

@ -172,6 +172,14 @@ make search case sensitive (insensitive by default):
```bash
export HH_CONFIG=casesensitive
```
show warnings:
```bash
export HH_CONFIG=warning
```
show debug messages:
```bash
export HH_CONFIG=warning
```
more colors and case sensitive search:
```bash
export HH_CONFIG=hicolor,casesensitive

View file

@ -9,19 +9,24 @@ uses shell history to provide suggest box like functionality
for commands used in the past. By default it parses .bash-history
file that is filtered as you type a command substring. Commands
are not just filtered, but also ordered by a ranking algorithm
that considers number of occurences, length and timestamp. In addition
hh allows removal of commands from history - for instance with a typo or with a sensitive content.
that considers number of occurences, length and timestamp.
Favorite and frequently used commands can be bookmarked. In addition
hh allows removal of commands from history - for instance with a typo
or with a sensitive content.
.SH OPTIONS
.TP
\fB--show-configuration\fR
Show configuration that can be added to .bashrc
.TP
\fB--help\fR
Show help
.TP
\fB--favorites\fR
Show favorites view immediately
.TP
\fB--show-configuration\fR
Show configuration that can be added to ~/.bashrc
.TP
\fB--version\fR
Show version information
.SH COMMANDS
.SH KEYS
.TP
\fBpattern\fR
Type to filter shell history.
@ -30,7 +35,7 @@ Type to filter shell history.
Toggle case sensitive search.
.TP
\fBCtrl\-/\fR
Toggle history as provided by shell vs. ranked history ordered by the number of occurences, length and timestamp.
Rotate view of history as provided by BASH, ranked history ordered by the number of occurences/length/timestamp and favorites.
.TP
\fBCtrl\-l\fR
Make search pattern lowercase or uppercase.
@ -68,14 +73,26 @@ Configuration options:
Get more colors with this option (default is monochromatic).
\fIcasesensitive\fR
Make the pattern-based filtering case sensitive by default.
Make the pattern-based filtering case sensitive (it's case insensitive by default).
\fIrawhistory\fR
Show normal history by default (default is metric-based).
Show normal history as a default view (metric-based view is shown otherwise).
\fIfavorites\fR
Show favorites as a default view (metric-based view is shown otherwise).
\fIwarning\fR
Show warning.
\fIdebug\fR
Show debug information.
Example:
\fBexport HH_CONFIG=casesensitive,hicolor\fR
.SH FILES
\fB~/.hh_favorites\fR bookmarked favorite commands
.SH CONFIGURATION
Optionally add the following lines to ~/.bashrc:
.nf

View file

@ -38,6 +38,7 @@
#define K_CTRL_A 1
#define K_CTRL_E 5
#define K_CTRL_F 6
#define K_CTRL_G 7
#define K_CTRL_H 8
#define K_CTRL_L 12
@ -60,10 +61,21 @@
#define HH_COLOR_PROMPT 3
#define HH_COLOR_DELETE 4
#define ENV_VAR_HH_CONFIG "HH_CONFIG"
#define HH_ENV_VAR_CONFIG "HH_CONFIG"
#define HH_CONFIG_HICOLOR "hicolor"
#define HH_CONFIG_CASE "casesensitive"
#define HH_CONFIG_SORTING "rawhistory"
#define HH_CONFIG_FAVORITES "favorites"
#define HH_CONFIG_DEBUG "debug"
#define HH_CONFIG_WARN "warning"
#define HH_DEBUG_LEVEL_NONE 0
#define HH_DEBUG_LEVEL_WARN 1
#define HH_DEBUG_LEVEL_DEBUG 2
#define HH_VIEW_RANKING 0
#define HH_VIEW_HISTORY 1
#define HH_VIEW_FAVORITES 2
#define SPACE_PADDING " "
@ -79,6 +91,11 @@
#define LOGCURSOR(Y)
#endif
static const char *HH_VIEW_LABELS[]={
"ranking",
"history",
"favorites"};
static const char *INSTALL_STRING=
"\n# add this configuration to ~/.bashrc"
"\nexport HH_CONFIG=hicolor # get more colors"
@ -95,7 +112,8 @@ static const char *HELP_STRING=
"Usage: hh [option] [arg1] [arg2]..."
"\nShell history suggest box:"
"\n"
"\n --show-configuration ... show configuration to be added to .bashrc"
"\n --favorites -f ... show command favorites"
"\n --show-configuration ... show configuration to be added to ~/.bashrc"
"\n --help ... display this help and exit"
"\n"
"\nReport bugs to martin.dvorak@mindforger.com"
@ -108,19 +126,20 @@ static const char *VERSION_STRING=
"\n";
static const char *LABEL_HELP=
"Type to filter, UP/DOWN to move, DEL to remove, TAB to select, C-g to cancel";
"Type to filter, UP/DOWN move, DEL remove, TAB select, C-f add favorite, C-g cancel";
static char **selection=NULL;
static unsigned selectionSize=0;
static bool caseSensitive=FALSE;
static bool defaultOrder=FALSE;
static int historyView=HH_VIEW_RANKING;
static bool hicolor=FALSE;
static int debugLevel=0;
static char screenLine[CMDLINE_LNG];
static char cmdline[CMDLINE_LNG];
void get_env_configuration()
{
char *hhconfig=getenv(ENV_VAR_HH_CONFIG);
char *hhconfig=getenv(HH_ENV_VAR_CONFIG);
if(hhconfig && strlen(hhconfig)>0) {
if(strstr(hhconfig,HH_CONFIG_HICOLOR)) {
hicolor=TRUE;
@ -129,7 +148,18 @@ void get_env_configuration()
caseSensitive=TRUE;
}
if(strstr(hhconfig,HH_CONFIG_SORTING)) {
defaultOrder=TRUE;
historyView=HH_VIEW_HISTORY;
} else {
if(strstr(hhconfig,HH_CONFIG_FAVORITES)) {
historyView=HH_VIEW_FAVORITES;
}
}
if(strstr(hhconfig,HH_CONFIG_DEBUG)) {
debugLevel=HH_DEBUG_LEVEL_DEBUG;
} else {
if(strstr(hhconfig,HH_CONFIG_WARN)) {
debugLevel=HH_DEBUG_LEVEL_WARN;
}
}
}
}
@ -181,12 +211,28 @@ void print_cmd_deleted_label(char *cmd, int occurences)
refresh();
}
void print_cmd_added_favorite_label(char *cmd)
{
snprintf(screenLine, getmaxx(stdscr), "Command '%s' added to favorites (C-/ to show favorites)", cmd);
if(hicolor) {
color_attr_on(COLOR_PAIR(4));
color_attr_on(A_BOLD);
}
mvprintw(Y_OFFSET_HELP, 0, screenLine);
if(hicolor) {
color_attr_off(A_BOLD);
color_attr_on(COLOR_PAIR(1));
}
clrtoeol();
refresh();
}
void print_history_label(HistoryItems *history)
{
int width=getmaxx(stdscr);
snprintf(screenLine, width, "- HISTORY - case:%s (C-t) - order:%s (C-/) - %d/%d ",
snprintf(screenLine, width, "- HISTORY - match:%s (C-t) - view:%s (C-/) - %d/%d ",
(caseSensitive?"sensitive":"insensitive"),
(defaultOrder?"history":"ranking"),
HH_VIEW_LABELS[historyView],
history->count,
history->rawCount);
width -= strlen(screenLine);
@ -241,8 +287,23 @@ unsigned make_selection(char *prefix, HistoryItems *history, int maxSelectionCou
realloc_selection(sizeof(char*) * maxSelectionCount);
unsigned i, selectionCount=0;
char **source=(defaultOrder?history->raw:history->items);
unsigned count=(defaultOrder?history->rawCount:history->count);
char **source;
unsigned count;
switch(historyView) {
case HH_VIEW_RANKING:
source=history->items;
count=history->count;
break;
case HH_VIEW_HISTORY:
source=history->raw;
count=history->rawCount;
break;
case HH_VIEW_FAVORITES:
source=history->favorites->items;
count=history->favorites->count;
break;
}
for(i=0; i<count && selectionCount<maxSelectionCount; i++) {
if(source[i]) {
@ -369,15 +430,19 @@ void highlight_selection(int selectionCursorPosition, int previousSelectionCurso
void selection_remove(char *cmd, HistoryItems *history)
{
if(history->count) {
int i, w;
for(i=0, w=0; i<history->count; i++) {
if(strcmp(history->items[i], cmd)) {
history->items[w]=history->items[i];
w++;
if(historyView==HH_VIEW_FAVORITES) {
favorites_remove(history->favorites, cmd);
} else {
if(history->count) {
int i, w;
for(i=0, w=0; i<history->count; i++) {
if(strcmp(history->items[i], cmd)) {
history->items[w]=history->items[i];
w++;
}
}
history->count=w;
}
history->count=w;
}
}
@ -396,6 +461,15 @@ void signal_callback_handler_ctrl_c(int signum)
}
}
int seletion_source_remove(char* delete, HistoryItems *history)
{
if(historyView!=HH_VIEW_FAVORITES) {
return history_mgmt_remove(delete);
} else {
return favorites_remove(history->favorites, delete);
}
}
void loop_to_select(HistoryItems *history)
{
signal(SIGINT, signal_callback_handler_ctrl_c);
@ -417,7 +491,7 @@ void loop_to_select(HistoryItems *history)
print_selection(get_max_history_items(stdscr), NULL, history);
color_attr_off(COLOR_PAIR(HH_COLOR_NORMAL));
bool done=FALSE, skip=TRUE, executeResult=FALSE, lowercase=TRUE, justDeleted=FALSE;
bool done=FALSE, skip=TRUE, executeResult=FALSE, lowercase=TRUE, printDefaultLabel=FALSE;
int basex=print_prompt(stdscr);
int x=basex, y=0, c, cursorX=0, cursorY=0, maxHistoryItems, deleteOccurences;
int width=getmaxx(stdscr);
@ -446,9 +520,9 @@ void loop_to_select(HistoryItems *history)
continue;
}
if(justDeleted) {
if(printDefaultLabel) {
print_help_label();
justDeleted=FALSE;
printDefaultLabel=FALSE;
}
switch (c) {
@ -458,11 +532,11 @@ void loop_to_select(HistoryItems *history)
msg=malloc(strlen(delete)+1);
strcpy(msg,delete);
selection_remove(delete, history);
deleteOccurences=history_mgmt_remove(delete);
deleteOccurences=seletion_source_remove(delete, history);
result=print_selection(maxHistoryItems, pattern, history);
print_cmd_deleted_label(msg, deleteOccurences);
move(y, basex+strlen(pattern));
justDeleted=TRUE;
printDefaultLabel=TRUE;
}
print_history_label(history);
break;
@ -473,11 +547,26 @@ void loop_to_select(HistoryItems *history)
selectionCursorPosition=0;
break;
case K_CTRL_SLASH:
defaultOrder=!defaultOrder;
historyView=(++historyView)%3;
result=print_selection(maxHistoryItems, pattern, history);
print_history_label(history);
selectionCursorPosition=0;
break;
case K_CTRL_F:
if(selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) {
result=selection[selectionCursorPosition];
if(historyView==HH_VIEW_FAVORITES) {
favorites_choose(history->favorites, result);
} else {
favorites_add(history->favorites, result);
print_cmd_added_favorite_label(result);
printDefaultLabel=TRUE;
}
result=print_selection(maxHistoryItems, pattern, history);
selectionCursorPosition=0;
}
break;
case KEY_RESIZE:
print_history_label(history);
result=print_selection(maxHistoryItems, pattern, history);
@ -618,7 +707,6 @@ void hstr()
HistoryItems *history=get_prioritized_history();
if(history) {
history_mgmt_open();
get_env_configuration();
loop_to_select(history);
hstr_on_exit();
} else {
@ -628,8 +716,12 @@ void hstr()
int main(int argc, char *argv[])
{
get_env_configuration();
if(argc>0) {
if(argc==2) {
if(strstr(argv[1], "--favorites") || strstr(argv[1], "-f")) {
historyView=HH_VIEW_FAVORITES;
}
if(strstr(argv[1], "--show-configuration")) {
printf("%s", INSTALL_STRING);
return EXIT_SUCCESS;

178
src/hstr_favorites.c Normal file
View file

@ -0,0 +1,178 @@
/*
============================================================================
Name : hstr_favorites.c
Author : martin.dvorak@midforger.com
Copyright : Apache 2.0
Description : Favorite commands.
============================================================================
*/
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "include/hstr_favorites.h"
#define FAVORITE_SEGMENT_SIZE 10
void favorites_init(FavoriteItems *favorites)
{
favorites->items=NULL;
favorites->count=0;
favorites->loaded=false;
}
char* favorites_get_filename()
{
char *home = getenv(ENV_VAR_HOME);
char *fileName = (char*) malloc(strlen(home) + 1 + strlen(FILE_HH_RC) + 1);
strcpy(fileName, home);
strcat(fileName, "/");
strcat(fileName, FILE_HH_RC);
return fileName;
}
void favorites_get(FavoriteItems *favorites)
{
if(!favorites->loaded) {
char* fileName = favorites_get_filename();
char *file_contents=NULL;
if(access(fileName, F_OK) != -1) {
long input_file_size;
FILE *input_file = fopen(fileName, "rb");
fseek(input_file, 0, SEEK_END);
input_file_size = ftell(input_file);
rewind(input_file);
file_contents = malloc((input_file_size + 1) * (sizeof(char)));
if(fread(file_contents, sizeof(char), input_file_size, input_file)==-1) {
exit(EXIT_FAILURE);
}
fclose(input_file);
file_contents[input_file_size] = 0;
if(file_contents && strlen(file_contents)) {
favorites->count = 0;
char *p=strchr(file_contents,'\n');
while (p!=NULL) {
favorites->count++;
p=strchr(p+1,'\n');
}
favorites->items = malloc(sizeof(char*) * favorites->count);
int i = 0;
char *pb=file_contents, *pe;
pe=strchr(file_contents, '\n');
while(pe!=NULL) {
favorites->items[i]=pb;
*pe=0;
favorites->items[i]=strdup(pb);
pb=pe+1;
pe=strchr(pb, '\n');
i++;
}
free(file_contents);
}
} else {
// favorites file not found > favorites don't exist yet
favorites->loaded=true;
return;
}
free(fileName);
}
}
void favorites_save(FavoriteItems *favorites)
{
char *fileName=favorites_get_filename();
if(favorites->count) {
FILE *output_file = fopen(fileName, "wb");
rewind(output_file);
int i;
for(i=0; i<favorites->count; i++) {
if(fwrite(favorites->items[i], sizeof(char), strlen(favorites->items[i]), output_file)==-1) {
exit(EXIT_FAILURE);
}
if(fwrite("\n", sizeof(char), strlen("\n"), output_file)==-1) {
exit(EXIT_FAILURE);
}
}
fclose(output_file);
} else {
if(access(fileName, F_OK) != -1) {
FILE *output_file = fopen(fileName, "wb");
fclose(output_file);
}
}
free(fileName);
}
void favorites_add(FavoriteItems *favorites, char *newFavorite)
{
if(favorites->count) {
favorites->items=realloc(favorites->items, sizeof(char *) * ++favorites->count);
favorites->items[favorites->count-1]=strdup(newFavorite);
favorites_choose(favorites, newFavorite);
} else {
favorites->items=malloc(sizeof(char*));
favorites->items[0]=strdup(newFavorite);
favorites->count=1;
}
favorites_save(favorites);
}
void favorites_choose(FavoriteItems *favorites, char *choice)
{
if(favorites->count && choice) {
int i;
char *b=0, *next;
for(i=0; i<favorites->count; i++) {
if(!strcmp(favorites->items[i],choice)) {
favorites->items[0]=favorites->items[i];
if(b) {
favorites->items[i]=b;
}
return;
}
next=favorites->items[i];
favorites->items[i]=b;
b=next;
}
}
favorites_save(favorites);
}
bool favorites_remove(FavoriteItems *favorites, char *almostDead)
{
if(favorites->count) {
int i, j;
for(i=0, j=0; i<favorites->count && j<favorites->count; i++, j++) {
if(!strcmp(favorites->items[i], almostDead)) {
j=i+1;
favorites->count--;
} else {
if(j>i) {
favorites->items[i]=favorites->items[j];
}
}
}
favorites_save(favorites);
return true;
} else {
return false;
}
}
void favorites_destroy(FavoriteItems *favorites)
{
if(favorites) {
int i;
for(i=0; i<favorites->count; i++) {
free(favorites->items[i]);
}
free(favorites);
}
}

View file

@ -47,8 +47,8 @@ char *get_history_file_name()
char *historyFile=getenv(ENV_VAR_HISTFILE);
if(!historyFile || strlen(historyFile)==0) {
char *home = getenv(ENV_VAR_HOME);
historyFile = malloc(strlen(home) + 1 + strlen(DEFAULT_HISTORY_FILE) + 1);
strcat(strcat(strcpy(historyFile, home), "/"), DEFAULT_HISTORY_FILE);
historyFile = malloc(strlen(home) + 1 + strlen(FILE_DEFAULT_HISTORY) + 1);
strcat(strcat(strcpy(historyFile, home), "/"), FILE_DEFAULT_HISTORY);
}
return historyFile;
}
@ -150,6 +150,13 @@ HistoryItems *get_prioritized_history()
radixsort_destroy(&rs);
FavoriteItems *favoriteItems=malloc(sizeof(FavoriteItems));
favorites_init(favoriteItems);
// TODO make favorites loading lazy > github issue
favorites_get(favoriteItems);
prioritizedHistory->favorites=favoriteItems;
return prioritizedHistory;
} else {
return NULL;
@ -159,6 +166,7 @@ HistoryItems *get_prioritized_history()
void free_prioritized_history()
{
free(prioritizedHistory->items);
favorites_destroy(prioritizedHistory->favorites);
free(prioritizedHistory);
}

View file

@ -0,0 +1,33 @@
/*
============================================================================
Name : hstr_favorites.h
Author : martin.dvorak@midforger.com
Copyright : Apache 2.0
Description : Favorite commands.
============================================================================
*/
#ifndef _HSTR_FAVORITES_H_
#define _HSTR_FAVORITES_H_
#include <stdlib.h>
#define ENV_VAR_USER "USER"
#define ENV_VAR_HOME "HOME"
#define FILE_HH_RC ".hh_favorites"
typedef struct {
char **items;
unsigned count;
bool loaded;
} FavoriteItems;
void favorites_init(FavoriteItems *favorites);
void favorites_get(FavoriteItems *favorites);
void favorites_add(FavoriteItems *favorites, char *favorite);
void favorites_choose(FavoriteItems *favorites, char *choice);
bool favorites_remove(FavoriteItems *favorites, char *almostDead);
void favorites_destroy(FavoriteItems *favorites);
#endif

View file

@ -17,21 +17,21 @@
#include <readline/history.h>
#include <unistd.h>
#include <stdbool.h>
#include "hstr_favorites.h"
#include "hstr_utils.h"
#include "hashset.h"
#include "radixsort.h"
#define ENV_VAR_USER "USER"
#define ENV_VAR_HOME "HOME"
#define ENV_VAR_HISTFILE "HISTFILE"
#define DEFAULT_HISTORY_FILE ".bash_history"
#define FILE_DEFAULT_HISTORY ".bash_history"
typedef struct {
char **items;
char **raw;
unsigned count;
char **raw;
unsigned rawCount;
FavoriteItems *favorites;
} HistoryItems;
HistoryItems *get_prioritized_history();

View file

@ -17,7 +17,11 @@
#include <stddef.h>
#include "hstr_utils.h"
#define SLOT_SIZE 1000
#define RADIX_SLOT_SIZE 1000
#define RADIX_DEBUG_LEVEL_NONE 0
#define RADIX_DEBUG_LEVEL_WARN 1
#define RADIX_DEBUG_LEVEL_DEBUG 2
typedef struct radixitem {
unsigned key;
@ -43,9 +47,11 @@ typedef struct {
RadixSlot **_slotDescriptors;
unsigned _slotsCount;
unsigned _topIndexLimit;
unsigned _debug;
} RadixSorter;
void radixsort_init(RadixSorter *rs, unsigned keyLimit);
void radixsort_set_debug_level(RadixSorter *rs, unsigned debugLevel);
void radixsort_add(RadixSorter *rs, RadixItem *item);
RadixItem *radix_cut(RadixSorter *rs, unsigned key, void *data);
RadixItem **radixsort_dump(RadixSorter *rs);

View file

@ -9,10 +9,8 @@
#include "include/radixsort.h"
#define GET_TOP_INDEX(KEY) KEY/SLOT_SIZE
#define GET_LOW_INDEX(KEY) KEY%SLOT_SIZE
#define RADIX_DEBUG 1
#define GET_TOP_INDEX(KEY) KEY/RADIX_SLOT_SIZE
#define GET_LOW_INDEX(KEY) KEY%RADIX_SLOT_SIZE
void radixsort_init(RadixSorter *rs, unsigned keyLimit)
{
@ -30,10 +28,15 @@ void radixsort_init(RadixSorter *rs, unsigned keyLimit)
rs->_slotsCount=0;
}
void radixsort_set_debug_level(RadixSorter *rs, unsigned debugLevel)
{
rs->_debug=debugLevel;
}
RadixItem **radixsort_get_slot(RadixSorter *rs, unsigned topIndex)
{
RadixItem **slot=malloc(SLOT_SIZE * sizeof(RadixItem *));
memset(slot, 0, SLOT_SIZE * sizeof(RadixItem *));
RadixItem **slot=malloc(RADIX_SLOT_SIZE * sizeof(RadixItem *));
memset(slot, 0, RADIX_SLOT_SIZE * sizeof(RadixItem *));
RadixSlot *descriptor=malloc(sizeof(RadixSlot));
descriptor->min=rs->keyLimit;
@ -48,7 +51,9 @@ RadixItem **radixsort_get_slot(RadixSorter *rs, unsigned topIndex)
void radixsort_add(RadixSorter *rs, RadixItem *item)
{
if(item->key > rs->keyLimit) {
if(RADIX_DEBUG) fprintf(stderr, "ERROR: Radix sort overflow - inserted key is bigger than limit (%u): %u\n", rs->keyLimit, item->key);
if(rs->_debug > RADIX_DEBUG_LEVEL_NONE) {
fprintf(stderr, "WARNING: Radix sort overflow - inserted key is bigger than limit (%u): %u\n", rs->keyLimit, item->key);
}
if(rs->optFloorAndInsertBigKeys) {
item->key = rs->keyLimit-1;
} else {
@ -174,7 +179,7 @@ void radixsort_stat(RadixSorter *rs, bool listing)
printf("\n Radixsort (size/max/limit/slot count): %u %u %u %u", rs->size, rs->maxKey, rs->keyLimit, rs->_slotsCount);
unsigned memory=rs->_topIndexLimit * sizeof(RadixItem ***);
memory+=memory;
memory+=rs->_slotsCount*(SLOT_SIZE * sizeof(RadixItem *));
memory+=rs->_slotsCount*(RADIX_SLOT_SIZE * sizeof(RadixItem *));
printf("\n Memory: %u\n", memory);
if(listing && rs->size>0) {
int t = GET_TOP_INDEX(rs->maxKey);