namefirst.diff (6036B)
1 # Description: Add git status column to detail mode. Provides additional 2 # command line flag -G which will render the git status 3 # column also in normal mode. Vim plugin users may consider 4 # adding the -G flag to their command override. 5 # Compatibility patch for the namefirst patch. 6 # 7 # Authors: Luuk van Baal 8 --- a/src/nnn.c 9 +++ b/src/nnn.c 10 @@ -298,6 +298,25 @@ 11 #define VFS_USED 1 12 #define VFS_SIZE 2 13 14 +/* Git icons */ 15 +#ifdef NERD 16 +#define GIT_ADD "" 17 +#define GIT_DEL "" 18 +#define GIT_IGN "" 19 +#define GIT_MOD "" 20 +#define GIT_NEW "" 21 +#define GIT_NON "-" 22 +#define GIT_UPD "" 23 +#else 24 +#define GIT_ADD "A" 25 +#define GIT_DEL "D" 26 +#define GIT_IGN "!" 27 +#define GIT_MOD "M" 28 +#define GIT_NEW "?" 29 +#define GIT_NON "-" 30 +#define GIT_UPD "U" 31 +#endif 32 + 33 /* TYPE DEFINITIONS */ 34 typedef unsigned int uint_t; 35 typedef unsigned char uchar_t; 36 @@ -322,6 +341,7 @@ 37 uid_t uid; /* 4 bytes */ 38 gid_t gid; /* 4 bytes */ 39 #endif 40 + char git_status[2][5]; 41 } *pEntry; 42 43 /* Selection marker */ 44 @@ -378,6 +398,7 @@ 45 uint_t cliopener : 1; /* All-CLI app opener */ 46 uint_t waitedit : 1; /* For ops that can't be detached, used EDITOR */ 47 uint_t rollover : 1; /* Roll over at edges */ 48 + uint_t normalgit : 1; /* Show git status in normal mode */ 49 } settings; 50 51 /* Non-persistent program-internal states (alphabeical order) */ 52 @@ -434,7 +455,17 @@ 53 ushort_t maxnameln, maxsizeln, maxuidln, maxgidln, maxentln, uidln, gidln, printguid; 54 } dtls; 55 56 +typedef struct { 57 + char status[2]; 58 + char path[PATH_MAX]; 59 +} git_status_t; 60 + 61 /* GLOBALS */ 62 +struct { 63 + bool show; 64 + size_t len; 65 + git_status_t *statuses; 66 +} git_statuses; 67 68 /* Configuration, contexts */ 69 static settings cfg = { 70 @@ -4341,6 +4372,47 @@ 71 return -1; 72 } 73 74 +static size_t get_git_statuses(const char *path) 75 +{ 76 + static char gst[] = "git -c core.quotePath= status -s --no-renames --ignored=matching -unormal . 2>/dev/null"; 77 + FILE *fp = popen(gst, "r"); 78 + char status[PATH_MAX]; 79 + size_t pathindex, i = -1; 80 + git_statuses.show = FALSE; 81 + 82 + while (fgets(status, PATH_MAX, fp)) { 83 + pathindex = (status[3] == '"') ? 4 : 3; 84 + if (!cfg.showhidden && status[pathindex] == '.') 85 + continue; 86 + status[xstrlen(status) - pathindex + 2] = '\0'; 87 + git_statuses.statuses = xrealloc(git_statuses.statuses, sizeof(git_status_t) * (++i + 1)); 88 + git_statuses.statuses[i].status[0] = status[0]; 89 + git_statuses.statuses[i].status[1] = status[1]; 90 + mkpath(path, status + pathindex, git_statuses.statuses[i].path); 91 + } 92 + 93 + pclose(fp); 94 + return (i + 1); 95 +} 96 + 97 +static void set_git_status(char status[][5], uint_t nr) 98 +{ 99 + for (int j = 0; j < 2; j++) { 100 + if (status[j][0] == '-') 101 + switch (git_statuses.statuses[nr].status[j]) { 102 + case ' ': xstrsncpy(status[j], GIT_NON, 4); break; 103 + case 'M': xstrsncpy(status[j], GIT_MOD, 4); break; 104 + case 'A': xstrsncpy(status[j], GIT_ADD, 4); break; 105 + case '?': xstrsncpy(status[j], GIT_NEW, 4); break; 106 + case '!': xstrsncpy(status[j], GIT_IGN, 4); break; 107 + case 'D': xstrsncpy(status[j], GIT_DEL, 4); break; 108 + case 'U': xstrsncpy(status[j], GIT_UPD, 4); break; 109 + } 110 + } 111 + if (git_statuses.statuses[nr].status[1] != '!') 112 + git_statuses.show = TRUE; 113 +} 114 + 115 static void resetdircolor(int flags) 116 { 117 /* Directories are always shown on top, clear the color when moving to first file */ 118 @@ -4765,6 +4837,10 @@ 119 120 uchar_t color_pair = get_color_pair_name_ind(ent, &ind, &attrs); 121 122 + if (git_statuses.show && (cfg.showdetail || cfg.normalgit)) 123 + printw("%*s%s%s", (cfg.normalgit && !cfg.showdetail) ? 1 : 0, "", 124 + ent->git_status[0], ent->git_status[1]); 125 + 126 addch((ent->flags & FILE_SELECTED) ? '+' | A_REVERSE | A_BOLD : ' '); 127 128 if (g_state.oldcolor) 129 @@ -6917,6 +6993,11 @@ 130 attron(COLOR_PAIR(cfg.curctx + 1)); 131 } 132 133 + char linkpath[PATH_MAX]; 134 + if ((git_statuses.len = get_git_statuses(path))) 135 + if (!realpath(path, linkpath)) 136 + printwarn(NULL); 137 + 138 #if _POSIX_C_SOURCE >= 200112L 139 posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); 140 #endif 141 @@ -7141,6 +7222,29 @@ 142 #endif 143 } 144 145 + if (git_statuses.len) { 146 + char dentpath[PATH_MAX]; 147 + size_t pathlen = mkpath(linkpath, dentp->name, dentpath); 148 + dentp->git_status[0][0] = dentp->git_status[1][0] = '-'; 149 + dentp->git_status[0][1] = dentp->git_status[1][1] = '\0'; 150 + 151 + if (dentp->flags & DIR_OR_DIRLNK) { 152 + char prefix[PATH_MAX]; 153 + memccpy(prefix, dentpath, '\0', PATH_MAX); 154 + prefix[pathlen - 1] = '/'; 155 + 156 + for (size_t i = 0; i < git_statuses.len; ++i) 157 + if (is_prefix(git_statuses.statuses[i].path, prefix, pathlen)) 158 + set_git_status(dentp->git_status, i); 159 + } else { 160 + for (size_t i = 0; i < git_statuses.len; ++i) 161 + if (!xstrcmp(git_statuses.statuses[i].path, dentpath)) { 162 + set_git_status(dentp->git_status, i); 163 + break; 164 + } 165 + } 166 + } 167 + 168 ++ndents; 169 } while ((dp = readdir(dirp))); 170 171 @@ -7715,7 +7819,8 @@ 172 cfg.showdetail ^= 1; 173 else /* 2 more accounted for below */ 174 n -= (dtls.maxentln - 2 - dtls.maxnameln); 175 - } 176 + } else if (cfg.normalgit && git_statuses.show) 177 + n -= 3; 178 179 /* 2 columns for preceding space and indicator */ 180 return (n - 2); 181 @@ -9576,6 +9681,7 @@ 182 " -F val fifo mode [0:preview 1:explore]\n" 183 #endif 184 " -g regex filters\n" 185 + " -G always show git status\n" 186 " -H show hidden files\n" 187 " -i show current file info\n" 188 " -J no auto-advance on selection\n" 189 @@ -9720,6 +9826,7 @@ 190 fflush(stdout); 191 } 192 #endif 193 + free(git_statuses.statuses); 194 free(selpath); 195 free(plgpath); 196 free(cfgpath); 197 @@ -9765,7 +9872,7 @@ 198 199 while ((opt = (env_opts_id > 0 200 ? env_opts[--env_opts_id] 201 - : getopt(argc, argv, "aAb:BcCdDeEfF:gHiJKl:nNop:P:QrRs:St:T:uUVxz0h"))) != -1) { 202 + : getopt(argc, argv, "aAb:BcCdDeEfF:gGHiJKl:nNop:P:QrRs:St:T:uUVxz0h"))) != -1) { 203 switch (opt) { 204 #ifndef NOFIFO 205 case 'a': 206 @@ -9819,6 +9926,9 @@ 207 cfg.regex = 1; 208 filterfn = &visible_re; 209 break; 210 + case 'G': 211 + cfg.normalgit = 1; 212 + break; 213 case 'H': 214 cfg.showhidden = 1; 215 break;