]> Sergey Matveev's repositories - nnn.git/blob - patches/gitstatus/namefirst.diff
patches/gitstatus: Fix nerd fonts broken by 3.0.0 update
[nnn.git] / patches / gitstatus / namefirst.diff
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
9 diff --git a/src/nnn.c b/src/nnn.c
10 index 88538787..d4af7c43 100644
11 --- a/src/nnn.c
12 +++ b/src/nnn.c
13 @@ -270,6 +270,25 @@
14  #define VFS_USED  1
15  #define VFS_SIZE  2
16
17 +/* Git icons */
18 +#ifdef NERD
19 +#define GIT_ADD ""
20 +#define GIT_DEL ""
21 +#define GIT_IGN ""
22 +#define GIT_MOD ""
23 +#define GIT_NEW ""
24 +#define GIT_NON "-"
25 +#define GIT_UPD "󰚰"
26 +#else
27 +#define GIT_ADD "A"
28 +#define GIT_DEL "D"
29 +#define GIT_IGN "!"
30 +#define GIT_MOD "M"
31 +#define GIT_NEW "?"
32 +#define GIT_NON "-"
33 +#define GIT_UPD "U"
34 +#endif
35 +
36  /* TYPE DEFINITIONS */
37  typedef unsigned int uint_t;
38  typedef unsigned char uchar_t;
39 @@ -294,6 +313,7 @@ typedef struct entry {
40         uid_t uid; /* 4 bytes */
41         gid_t gid; /* 4 bytes */
42  #endif
43 +       char git_status[2][5];
44  } *pEntry;
45
46  /* Selection marker */
47 @@ -349,6 +369,7 @@ typedef struct {
48         uint_t cliopener  : 1;  /* All-CLI app opener */
49         uint_t waitedit   : 1;  /* For ops that can't be detached, used EDITOR */
50         uint_t rollover   : 1;  /* Roll over at edges */
51 +       uint_t normalgit  : 1;  /* Show git status in normal mode */
52  } settings;
53
54  /* Non-persistent program-internal states (alphabeical order) */
55 @@ -404,7 +425,17 @@ static struct {
56         ushort_t maxnameln, maxsizeln, maxuidln, maxgidln, maxentln, uidln, gidln, printguid;
57  } dtls;
58
59 +typedef struct {
60 +       char status[2];
61 +       char path[PATH_MAX];
62 +} git_status_t;
63 +
64  /* GLOBALS */
65 +struct {
66 +       bool show;
67 +       size_t len;
68 +       git_status_t *statuses;
69 +} git_statuses;
70
71  /* Configuration, contexts */
72  static settings cfg = {
73 @@ -3804,6 +3835,47 @@ static int get_kv_key(kv *kvarr, char *val, uchar_t max, uchar_t id)
74         return -1;
75  }
76
77 +static size_t get_git_statuses(const char *path)
78 +{
79 +       static char gst[] = "git -c core.quotePath= status -s --no-renames --ignored=matching -unormal . 2>/dev/null";
80 +       FILE *fp = popen(gst, "r");
81 +       char status[PATH_MAX];
82 +       size_t pathindex, i = -1;
83 +       git_statuses.show = FALSE;
84 +
85 +       while (fgets(status, PATH_MAX, fp)) {
86 +               pathindex = (status[3] == '"') ? 4 : 3;
87 +               if (!cfg.showhidden && status[pathindex] == '.')
88 +                       continue;
89 +               status[xstrlen(status) - pathindex + 2] = '\0';
90 +               git_statuses.statuses = xrealloc(git_statuses.statuses, sizeof(git_status_t) * (++i + 1));
91 +               git_statuses.statuses[i].status[0] = status[0];
92 +               git_statuses.statuses[i].status[1] = status[1];
93 +               mkpath(path, status + pathindex, git_statuses.statuses[i].path);
94 +       }
95 +
96 +       pclose(fp);
97 +       return (i + 1);
98 +}
99 +
100 +static void set_git_status(char status[][5], uint_t nr)
101 +{
102 +       for (int j = 0; j < 2; j++) {
103 +               if (status[j][0] == '-')
104 +                       switch (git_statuses.statuses[nr].status[j]) {
105 +                               case ' ': xstrsncpy(status[j], GIT_NON, 4); break;
106 +                               case 'M': xstrsncpy(status[j], GIT_MOD, 4); break;
107 +                               case 'A': xstrsncpy(status[j], GIT_ADD, 4); break;
108 +                               case '?': xstrsncpy(status[j], GIT_NEW, 4); break;
109 +                               case '!': xstrsncpy(status[j], GIT_IGN, 4); break;
110 +                               case 'D': xstrsncpy(status[j], GIT_DEL, 4); break;
111 +                               case 'U': xstrsncpy(status[j], GIT_UPD, 4); break;
112 +                       }
113 +       }
114 +       if (git_statuses.statuses[nr].status[1] != '!')
115 +               git_statuses.show = TRUE;
116 +}
117 +
118  static void resetdircolor(int flags)
119  {
120         /* Directories are always shown on top, clear the color when moving to first file */
121 @@ -4104,6 +4176,9 @@ static void printent(const struct entry *ent, uint_t namecols, bool sel)
122         int attrs = 0, namelen;
123         uchar_t color_pair = get_color_pair_name_ind(ent, &ind, &attrs);
124
125 +       if (git_statuses.show && (cfg.showdetail || cfg.normalgit))
126 +               printw(" %s%s", ent->git_status[0], ent->git_status[1]);
127 +
128         addch((ent->flags & FILE_SELECTED) ? '+' | A_REVERSE | A_BOLD : ' ');
129
130         if (g_state.oldcolor)
131 @@ -5598,6 +5673,11 @@ static int dentfill(char *path, struct entry **ppdents)
132                 attron(COLOR_PAIR(cfg.curctx + 1));
133         }
134
135 +       char linkpath[PATH_MAX];
136 +       if ((git_statuses.len = get_git_statuses(path)))
137 +               if (!realpath(path, linkpath))
138 +                       printwarn(NULL);
139 +
140  #if _POSIX_C_SOURCE >= 200112L
141         posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
142  #endif
143 @@ -5798,6 +5878,29 @@ static int dentfill(char *path, struct entry **ppdents)
144  #endif
145                 }
146
147 +               if (git_statuses.len) {
148 +                       char dentpath[PATH_MAX];
149 +                       size_t pathlen = mkpath(linkpath, dentp->name, dentpath);
150 +                       dentp->git_status[0][0] = dentp->git_status[1][0] = '-';
151 +                       dentp->git_status[0][1] = dentp->git_status[1][1] = '\0';
152 +
153 +                       if (dentp->flags & DIR_OR_DIRLNK) {
154 +                               char prefix[PATH_MAX];
155 +                               memccpy(prefix, dentpath, '\0', PATH_MAX);
156 +                               prefix[pathlen - 1] = '/';
157 +
158 +                               for (size_t i = 0; i < git_statuses.len; ++i)
159 +                                       if (is_prefix(git_statuses.statuses[i].path, prefix, pathlen))
160 +                                               set_git_status(dentp->git_status, i);
161 +                       } else {
162 +                               for (size_t i = 0; i < git_statuses.len; ++i)
163 +                                       if (!xstrcmp(git_statuses.statuses[i].path, dentpath)) {
164 +                                               set_git_status(dentp->git_status, i);
165 +                                               break;
166 +                                       }
167 +                       }
168 +               }
169 +
170                 ++ndents;
171         } while ((dp = readdir(dirp)));
172
173 @@ -6362,7 +6465,8 @@ static int adjust_cols(int n)
174                         cfg.showdetail ^= 1;
175                 else /* 2 more accounted for below */
176                         n -= (dtls.maxentln - 2 - dtls.maxnameln);
177 -       }
178 +       } else if (cfg.normalgit && git_statuses.show)
179 +               n -= 3;
180
181         /* 2 columns for preceding space and indicator */
182         return (n - 2);
183 @@ -6517,7 +6621,7 @@ static void redraw(char *path)
184                         }
185  #endif
186                 }
187 -               dtls.maxentln = dtls.maxnameln + dtls.maxsizeln + (dtls.printguid ? (dtls.maxuidln + dtls.maxgidln + 29) : 26);
188 +               dtls.maxentln = dtls.maxnameln + dtls.maxsizeln + (dtls.printguid ? (dtls.maxuidln + dtls.maxgidln + 3) : 0) + (git_statuses.show ? 29 : 26);
189         }
190
191         ncols = adjust_cols(ncols);
192 @@ -8153,6 +8257,7 @@ static void usage(void)
193                 " -F val  fifo mode [0:preview 1:explore]\n"
194  #endif
195                 " -g      regex filters\n"
196 +               " -G      always show git status\n"
197                 " -H      show hidden files\n"
198                 " -i      show current file info\n"
199                 " -J      no auto-advance on selection\n"
200 @@ -8292,6 +8397,7 @@ static void cleanup(void)
201                 fflush(stdout);
202         }
203  #endif
204 +       free(git_statuses.statuses);
205         free(selpath);
206         free(plgpath);
207         free(cfgpath);
208 @@ -8336,7 +8442,7 @@ int main(int argc, char *argv[])
209
210         while ((opt = (env_opts_id > 0
211                        ? env_opts[--env_opts_id]
212 -                      : getopt(argc, argv, "aAb:BcCdDeEfF:gHiJKl:nop:P:QrRs:St:T:uUVxh"))) != -1) {
213 +                      : getopt(argc, argv, "aAb:BcCdDeEfF:gGHiJKl:nop:P:QrRs:St:T:uUVxh"))) != -1) {
214                 switch (opt) {
215  #ifndef NOFIFO
216                 case 'a':
217 @@ -8390,6 +8496,9 @@ int main(int argc, char *argv[])
218                         cfg.regex = 1;
219                         filterfn = &visible_re;
220                         break;
221 +               case 'G':
222 +                       cfg.normalgit = 1;
223 +                       break;
224                 case 'H':
225                         cfg.showhidden = 1;
226                         break;