Merge pull request #530 from jjlin/master
[tig.git] / src / watch.c
bloba31cb0a58a2b46b1443d451e6658cbd27a6b5999
1 /* Copyright (c) 2006-2015 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->changed = WATCH_NONE;
33 watch->state = WATCH_NONE;
36 void
37 watch_unregister(struct watch *watch)
39 struct watch *pos, *prev = NULL;
41 for (pos = watches; pos; prev = pos, pos = pos->next) {
42 if (watch != pos)
43 continue;
44 if (!prev)
45 watches = watch->next;
46 else
47 prev->next = watch->next;
48 break;
51 memset(watch, 0, sizeof(*watch));
54 struct watch_handler {
55 enum watch_trigger (*check)(struct watch_handler *handler, enum watch_event event, enum watch_trigger check);
56 enum watch_trigger triggers;
57 time_t last_modified;
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 const struct ref *head;
83 if (check_file_mtime(&handler->last_modified, "%s/HEAD", repo.git_dir))
84 return WATCH_HEAD;
86 // FIXME: check branch
87 if ((head = get_ref_head()) &&
88 check_file_mtime(&handler->last_modified, "%s/refs/head/%s", repo.git_dir, head->name))
89 return WATCH_HEAD;
91 return WATCH_NONE;
94 static enum watch_trigger
95 watch_stash_handler(struct watch_handler *handler, enum watch_event event, enum watch_trigger check)
97 if (check_file_mtime(&handler->last_modified, "%s/refs/stash", repo.git_dir))
98 return WATCH_STASH;
100 return WATCH_NONE;
103 static enum watch_trigger
104 watch_index_handler(struct watch_handler *handler, enum watch_event event, enum watch_trigger check)
106 enum watch_trigger changed = WATCH_NONE;
107 struct index_diff diff;
109 if (event == WATCH_EVENT_AFTER_COMMAND)
110 return check_file_mtime(&handler->last_modified, "%s/index", repo.git_dir)
111 ? check : WATCH_NONE;
113 if (!check_file_mtime(&handler->last_modified, "%s/index", repo.git_dir) ||
114 event == WATCH_EVENT_SWITCH_VIEW ||
115 !index_diff(&diff, false, false))
116 return WATCH_NONE;
118 if (check & WATCH_INDEX_STAGED) {
119 if (diff.staged)
120 changed |= WATCH_INDEX_STAGED_YES;
121 else
122 changed |= WATCH_INDEX_STAGED_NO;
125 if (check & WATCH_INDEX_UNSTAGED) {
126 if (diff.unstaged)
127 changed |= WATCH_INDEX_UNSTAGED_YES;
128 else
129 changed |= WATCH_INDEX_UNSTAGED_NO;
132 if (changed)
133 handler->last_modified = time(NULL);
135 return changed;
138 static enum watch_trigger
139 watch_refs_handler(struct watch_handler *handler, enum watch_event event,
140 enum watch_trigger check)
142 if (event == WATCH_EVENT_AFTER_COMMAND)
143 load_refs(true);
145 return WATCH_NONE;
148 static struct watch_handler watch_handlers[] = {
149 { watch_index_handler, WATCH_INDEX },
150 { watch_head_handler, WATCH_HEAD },
151 { watch_stash_handler, WATCH_STASH },
152 { watch_refs_handler, WATCH_HEAD | WATCH_REFS },
155 static bool
156 watch_no_refresh(enum watch_event event)
158 return opt_refresh_mode == REFRESH_MODE_MANUAL ||
159 (opt_refresh_mode == REFRESH_MODE_AFTER_COMMAND &&
160 event != WATCH_EVENT_AFTER_COMMAND);
163 static void
164 watch_apply_changes(struct watch *source, enum watch_event event,
165 enum watch_trigger changed)
167 struct watch *watch;
169 if (watch_no_refresh(event))
170 return;
172 for (watch = watches; watch; watch = watch->next) {
173 enum watch_trigger triggers = changed & watch->triggers;
175 if (source == watch) {
176 source->state |= triggers;
177 continue;
180 if (event == WATCH_EVENT_AFTER_COMMAND) {
181 watch->state = WATCH_NONE;
182 triggers = watch->triggers;
185 watch->changed |= triggers;
189 void
190 watch_apply(struct watch *source, enum watch_trigger changed)
192 watch_apply_changes(source, WATCH_EVENT_LOAD, changed);
195 static enum watch_trigger
196 watch_update_event(enum watch_event event, enum watch_trigger trigger,
197 enum watch_trigger changed)
199 time_t timestamp = 0;
200 int i;
202 if (watch_no_refresh(event))
203 return WATCH_NONE;
205 if (event == WATCH_EVENT_AFTER_COMMAND)
206 timestamp = time(NULL);
208 for (i = 0; i < ARRAY_SIZE(watch_handlers); i++) {
209 struct watch_handler *handler = &watch_handlers[i];
211 if (event == WATCH_EVENT_AFTER_COMMAND) {
212 changed = handler->triggers;
213 handler->last_modified = timestamp;
214 continue;
217 if (*repo.git_dir &&
218 (trigger & handler->triggers) &&
219 (changed | handler->triggers) != changed)
220 changed |= handler->check(handler, event, trigger);
223 if (changed)
224 watch_apply_changes(NULL, event, changed);
226 return changed;
229 #define watch_trigger_unmask(triggers, set) ((triggers) & ~(set))
231 static inline enum watch_trigger
232 watch_unchanged_triggers(struct watch *watch)
234 return watch_trigger_unmask(watch->triggers, watch->changed);
237 enum watch_trigger
238 watch_update_single(struct watch *watch, enum watch_event event)
240 enum watch_trigger trigger = watch_unchanged_triggers(watch);
242 return watch_update_event(event, trigger, watch->changed);
245 enum watch_trigger
246 watch_update(enum watch_event event)
248 enum watch_trigger trigger = WATCH_NONE;
249 struct watch *watch;
251 /* Collect triggers to check. Skkipping watches that are already
252 * marked dirty to avoid unnecessary checks. */
253 for (watch = watches; watch; watch = watch->next)
254 trigger |= watch_unchanged_triggers(watch);
256 return watch_update_event(event, trigger, WATCH_NONE);
260 watch_periodic(int interval)
262 static time_t last_update;
263 int delay = -1;
265 if (watches && interval > 0) {
266 time_t now = time(NULL);
268 if (!last_update)
269 last_update = now;
270 if (last_update + interval <= now) {
271 watch_update(WATCH_EVENT_PERIODIC);
272 last_update = now;
275 delay = (now - last_update + interval) * 1000;
278 return delay;
281 bool
282 watch_dirty(struct watch *watch)
284 enum watch_trigger old_index = watch->state & WATCH_INDEX;
285 enum watch_trigger new_index = watch->changed & WATCH_INDEX;
286 enum watch_trigger index = watch_trigger_unmask(new_index, old_index);
287 enum watch_trigger other = watch_trigger_unmask(watch->changed, WATCH_INDEX);
288 bool dirty = !!(index | other);
290 watch->changed = WATCH_NONE;
291 return dirty;
294 /* vim: set ts=8 sw=8 noexpandtab: */