Auto-refresh views when changes are detected in the repository
[tig.git] / src / watch.c
blob54ff22c5dc00b093f1f07860d3722df0205d41ce
1 /* Copyright (c) 2006-2014 Jonas Fonseca <jonas.fonseca@gmail.com>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include "tig/tig.h"
15 #include "tig/repo.h"
16 #include "tig/refdb.h"
17 #include "tig/io.h"
18 #include "tig/options.h"
19 #include "tig/watch.h"
21 static struct watch *watches;
23 void
24 watch_register(struct watch *watch, enum watch_trigger triggers)
26 watch_unregister(watch);
28 watch->next = watches;
29 watches = watch;
31 watch->triggers = triggers;
32 watch->dirty = FALSE;
35 void
36 watch_unregister(struct watch *watch)
38 struct watch *pos, *prev = NULL;
40 for (pos = watches; pos; prev = pos, pos = pos->next) {
41 if (watch != pos)
42 continue;
43 if (!prev)
44 watches = watch->next;
45 else
46 prev->next = watch->next;
47 break;
50 memset(watch, 0, sizeof(*watch));
53 struct watch_handler {
54 enum watch_trigger (*check)(struct watch_handler *handler, enum watch_event event, enum watch_trigger check);
55 enum watch_trigger triggers;
56 time_t last_modified;
57 enum watch_trigger last_check;
60 static bool
61 check_file_mtime(time_t *last_modified, const char *path_fmt, ...)
63 char path[SIZEOF_STR];
64 struct stat stat;
65 int retval;
67 FORMAT_BUFFER(path, sizeof(path), path_fmt, retval, FALSE);
69 if (retval < 0 ||
70 lstat(path, &stat) < 0 ||
71 stat.st_mtime <= *last_modified)
72 return FALSE;
74 *last_modified = stat.st_mtime;
75 return TRUE;
78 static enum watch_trigger
79 watch_head_handler(struct watch_handler *handler, enum watch_event event, enum watch_trigger check)
81 struct ref *head;
83 if (*repo.git_dir &&
84 check_file_mtime(&handler->last_modified, "%s/HEAD", repo.git_dir))
85 return WATCH_HEAD;
87 // FIXME: check branch
88 if ((head = get_ref_head()) &&
89 check_file_mtime(&handler->last_modified, "%s/refs/head/%s", repo.git_dir, head->name))
90 return WATCH_HEAD;
92 return WATCH_NONE;
95 static enum watch_trigger
96 watch_stash_handler(struct watch_handler *handler, enum watch_event event, enum watch_trigger check)
98 if (*repo.git_dir &&
99 check_file_mtime(&handler->last_modified, "%s/refs/stash", repo.git_dir))
100 return WATCH_STASH;
102 return WATCH_NONE;
105 static enum watch_trigger
106 watch_index_handler(struct watch_handler *handler, enum watch_event event, enum watch_trigger check)
108 enum watch_trigger changed = WATCH_NONE;
109 enum watch_trigger diff = WATCH_NONE;
111 if (!*repo.git_dir)
112 return WATCH_NONE;
114 if (event == WATCH_EVENT_AFTER_EXTERNAL)
115 return check_file_mtime(&handler->last_modified, "%s/index", repo.git_dir)
116 ? check : WATCH_NONE;
118 if (!check_file_mtime(&handler->last_modified, "%s/index", repo.git_dir) ||
119 !update_index())
120 return WATCH_NONE;
122 if (check & WATCH_INDEX_STAGED) {
123 if (index_diff_staged())
124 changed |= WATCH_INDEX_STAGED;
125 else if (handler->last_check & WATCH_INDEX_STAGED)
126 diff |= WATCH_INDEX_STAGED;
129 if (check & WATCH_INDEX_UNSTAGED) {
130 if (index_diff_unstaged())
131 changed |= WATCH_INDEX_UNSTAGED;
132 else if (handler->last_check & WATCH_INDEX_UNSTAGED)
133 diff |= WATCH_INDEX_UNSTAGED;
136 handler->last_check = changed;
137 changed |= diff;
139 if (changed)
140 handler->last_modified = time(NULL);
142 return changed;
145 static struct watch_handler watch_handlers[] = {
146 { watch_index_handler, WATCH_INDEX_STAGED | WATCH_INDEX_UNSTAGED },
147 { watch_head_handler, WATCH_HEAD },
148 { watch_stash_handler, WATCH_STASH },
151 enum watch_trigger
152 watch_update(enum watch_event event)
154 enum watch_trigger trigger = WATCH_NONE;
155 enum watch_trigger changed = WATCH_NONE;
156 struct watch *watch;
157 int i;
159 if (opt_refresh_mode == REFRESH_MODE_MANUEL)
160 return changed;
162 /* Collect triggers to check. Skkipping watches that are already
163 * marked dirty to avoid unnecessary checks. */
164 for (watch = watches; watch; watch = watch->next)
165 if (!watch->dirty)
166 trigger |= watch->triggers;
168 for (i = 0; trigger && i < ARRAY_SIZE(watch_handlers); i++) {
169 struct watch_handler *handler = &watch_handlers[i];
171 if (trigger & handler->triggers)
172 changed |= handler->check(handler, event, trigger);
175 for (watch = watches; watch; watch = watch->next)
176 if (changed & watch->triggers)
177 watch->dirty = TRUE;
179 return changed;
183 watch_periodic(int interval)
185 static time_t last_update;
186 int delay = -1;
188 if (watches && interval > 0) {
189 time_t now = time(NULL);
191 if (!last_update)
192 last_update = now;
193 if (last_update + interval <= now) {
194 watch_update(WATCH_EVENT_PERIODIC);
195 last_update = now;
198 delay = (now - last_update + interval) * 1000;
201 return delay;
204 bool
205 watch_dirty(struct watch *watch)
207 bool dirty = FALSE;
209 if (watch) {
210 dirty = watch->dirty;
211 watch->dirty = FALSE;
214 return dirty;
217 /* vim: set ts=8 sw=8 noexpandtab: */