git-stats

Command line utility showing plots of git commits over time in the terminal
git clone https://noulin.net/git/git-stats.git
Log | Files | Refs | README

commit 268b91a3214f8035721190e5fe8016f6f0a0d513
parent e2facf9d06fe71e5992132faedcb80f2aac97901
Author: Remy Noulin <loader2x@gmail.com>
Date:   Fri,  4 Jun 2021 21:54:13 +0200

display plot with braille dots

add from date argument

git-stats.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 96 insertions(+), 26 deletions(-)

Diffstat:
Mgit-stats.c | 122++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 96 insertions(+), 26 deletions(-)

diff --git a/git-stats.c b/git-stats.c @@ -52,14 +52,29 @@ int main(int ARGC, char** ARGV) { // fill the display with the plot // show plot + char *from = null; + if (argc > 1) { + from = argv[1]; + } + // get git statistics cleanListP(dates) = execOut("git log --pretty=format:\"%ai\""); - var c = lenG(dates); - // sort the dates sortG(&dates); + int cut = 0; + enumerateS(dates, s, i) { + if (startsWithG(s, from)) { + cut = i; + break; + } + } + + if (cut) { + delG(&dates, 0, cut); + } + // remove time, keep only the date in a slice: slDates // count daily commits typ struct { @@ -131,7 +146,7 @@ int main(int ARGC, char** ARGV) { sliceForEach(&slDates, date) { time_t t = strToUnixTime(date->date, "%Y-%m-%d"); - int col = (f64)(t-earliest)/(f64)(latest-earliest) * (f64)(wz.cols-1/*-1 to make sure it writes inside the display*/); + int col = (f64)(t-earliest)/(f64)(latest-earliest) * (f64)(wz.cols*2-1/*-1 to make sure it writes inside the display*/); if (col == p.col) { p.value = MAX(date->count, p.value); } @@ -144,48 +159,103 @@ int main(int ARGC, char** ARGV) { sliceAppend(&slPl, p); // create a blank display array - char *display[wz.cols][graphHeight]; + u32 dotDisplay[wz.cols][graphHeight]; - char *BARS[] = {" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"}; - - // clear display + // clear dotDisplay range(i, wz.cols) { range(j, graphHeight) { - display[i][j] = BARS[0]; + dotDisplay[i][j] = ' '; } } + // for more information about braille, see: + // https://en.wikipedia.org/wiki/Braille_Patterns + const u32 brailleOffset = 0x2800; + const u32 braille[4][2] = { + {0x01, 0x08}, + {0x02, 0x10}, + {0x04, 0x20}, + {0x40, 0x80}, + }; + + void setPoint(i32 x, i32 y) { + dotDisplay[x/2][y/4] |= braille[y%4][x%2] + brailleOffset; + } - // fill the display with the plot - sliceForEach(&slPl, elm) { - int dpHeight = (f64)(graphHeight*8) - (f64)elm->value/(f64)maxCount * (f64)(graphHeight*8); - int bits = dpHeight & 7; - int cbar = 8 - bits; - - // draw bar - int h = dpHeight / 8; - - if (bits) { - // print BARS[cbar] - display[elm->col][h] = BARS[cbar]; - inc h; + void line(i32 x0, i32 y0, i32 x1, i32 y1) { + // bresenham's line algorithm + // https://en.wikipedia.org/wiki/Bresenham's_line_algorithm + i32 dx = abs(x1-x0); + i32 sx = x0<x1 ? 1 : -1; + i32 dy = -abs(y1-y0); + i32 sy = y0<y1 ? 1 : -1; + // error value e_xy + i32 err = dx+dy; + loopBreakerInit; + forever { + loopBreaker(10000); + setPoint(x0, y0); + if (x0 == x1 and y0 == y1) break; + i32 e2 = 2*err; + if (e2 >= dy) { + // e_xy+e_x > 0 + err += dy; + x0 += sx; + } + if (e2 <= dx) { + // e_xy+e_y < 0 + err += dx; + y0 += sy; + } } + } - rangeFrom(i, h, graphHeight) { - display[elm->col][i] = BARS[8]; - } + // fill the display with the plot + sliceEnumerate(&slPl, sli, elm) { + if (sli > 0) { + int dpHeight = (f64)(graphHeight*4) - (f64)elm->value/(f64)maxCount * (f64)(graphHeight*4); + int prevHeight = (f64)(graphHeight*4) - (f64)sliceAt(&slPl,sli-1).value/(f64)maxCount * (f64)(graphHeight*4); + line(sliceAt(&slPl,sli-1).col, prevHeight, elm->col, dpHeight); + } //logI("col %d, value %d", elm->col, elm->value); - //logI("col %3d, value %4d %3d cbar %d bits %d", elm->col, dpHeight, h, cbar, bits); } // show plot + char maxC[20] = {0}; + snprintf(maxC, sizeof(maxC), "%d", maxCount); + var ln = strlen(maxC); + snprintf(maxC, sizeof(maxC), BLD"%d"RST, maxCount); + #define HORIZONTAL_DASH "┈" + printf(maxC); + rangeFrom(i, ln, wz.cols) { + printf(HORIZONTAL_DASH); + } + put; range(j, graphHeight) { range(i, wz.cols) { - printf(display[i][j]); + char dots[10] = {0}; + bRune2CodeUTF8(dots, dotDisplay[i][j]); + printf(BLD"%s"RST, dots); } put; } + range(i, wz.cols) { + printf(HORIZONTAL_DASH); + } + put; + int col = strlen(sliceFirst(&slDates).date); + printf(BLD"%s"RST, sliceFirst(&slDates).date); + time_t halfTime = (earliest+latest)/2; + char hT[5]; + bLTimeToYMDS(hT, sizeof(hT), halfTime); + int spaces = (wz.cols/2-2)-col; + col += spaces + 4 /*halfTime string length*/; + range(i, spaces) printf(" "); + printf(BLD"%s"RST, hT); + spaces = wz.cols - strlen(sliceLast(&slDates).date) - col; + range(i, spaces) printf(" "); + printf(BLD"%s"RST, sliceLast(&slDates).date); sliceFree(&slPl);