Mercurial > hg > easyhg
comparison src/fswatcher.cpp @ 540:fc2df97920e8 fswatcher
Introduce FsWatcher into main window (though it isn't fully rigged up yet)
author | Chris Cannam |
---|---|
date | Mon, 13 Feb 2012 17:29:06 +0000 |
parents | 3935a7e621ca |
children | 0a16db274f2c |
comparison
equal
deleted
inserted
replaced
539:3935a7e621ca | 540:fc2df97920e8 |
---|---|
14 License, or (at your option) any later version. See the file | 14 License, or (at your option) any later version. See the file |
15 COPYING included with this distribution for more information. | 15 COPYING included with this distribution for more information. |
16 */ | 16 */ |
17 | 17 |
18 #include "fswatcher.h" | 18 #include "fswatcher.h" |
19 #include "debug.h" | |
19 | 20 |
20 #include <QMutexLocker> | 21 #include <QMutexLocker> |
21 #include <QDir> | 22 #include <QDir> |
22 | 23 |
23 #include <deque> | 24 #include <deque> |
25 | |
26 #define DEBUG_FSWATCHER 1 | |
24 | 27 |
25 /* | 28 /* |
26 * Watching the filesystem is trickier than it seems at first glance. | 29 * Watching the filesystem is trickier than it seems at first glance. |
27 * | 30 * |
28 * We ideally should watch every directory, and every file that is | 31 * We ideally should watch every directory, and every file that is |
34 * filesystem. This can happen even in "read-only" operations: for | 37 * filesystem. This can happen even in "read-only" operations: for |
35 * example, hg stat creates files called hg-checklink and hg-checkexec | 38 * example, hg stat creates files called hg-checklink and hg-checkexec |
36 * to test properties of the filesystem. So we need to know to ignore | 39 * to test properties of the filesystem. So we need to know to ignore |
37 * those files; unfortunately, when watching a directory (which is how | 40 * those files; unfortunately, when watching a directory (which is how |
38 * we find out about the creation of new files) we are notified only | 41 * we find out about the creation of new files) we are notified only |
39 * that the directory has changed -- we aren't told what changed. So | 42 * that the directory has changed -- we aren't told what changed. |
40 * we need to rescan the directory merely to find out whether to | 43 * |
41 * ignore the change or not. | 44 * This means that, when a directory changes, we need to rescan the |
45 * directory to learn whether the set of files in it _excluding_ files | |
46 * matching our ignore patterns differs from the previous scan, and | |
47 * ignore the change if it doesn't. | |
42 */ | 48 */ |
43 | 49 |
44 FsWatcher::FsWatcher() : | 50 FsWatcher::FsWatcher() : |
45 m_lastToken(0), | 51 m_lastToken(0), |
46 m_lastCounter(0) | 52 m_lastCounter(0) |
61 QMutexLocker locker(&m_mutex); | 67 QMutexLocker locker(&m_mutex); |
62 m_watcher.removePaths(m_watcher.directories()); | 68 m_watcher.removePaths(m_watcher.directories()); |
63 m_watcher.removePaths(m_watcher.files()); | 69 m_watcher.removePaths(m_watcher.files()); |
64 m_workDirPath = path; | 70 m_workDirPath = path; |
65 addWorkDirectory(path); | 71 addWorkDirectory(path); |
72 debugPrint(); | |
66 } | 73 } |
67 | 74 |
68 void | 75 void |
69 FsWatcher::setTrackedFilePaths(QStringList paths) | 76 FsWatcher::setTrackedFilePaths(QStringList paths) |
70 { | 77 { |
71 QMutexLocker locker(&m_mutex); | 78 QMutexLocker locker(&m_mutex); |
72 m_watcher.removePaths(m_watcher.files()); | 79 m_watcher.removePaths(m_watcher.files()); |
73 foreach (QString path, paths) { | 80 foreach (QString path, paths) { |
74 m_watcher.addPath(path); | 81 m_watcher.addPath(path); |
75 } | 82 } |
83 debugPrint(); | |
76 } | 84 } |
77 | 85 |
78 void | 86 void |
79 FsWatcher::addWorkDirectory(QString path) | 87 FsWatcher::addWorkDirectory(QString path) |
80 { | 88 { |
92 | 100 |
93 QString path = pending.front(); | 101 QString path = pending.front(); |
94 pending.pop_front(); | 102 pending.pop_front(); |
95 if (!alreadyWatched.contains(path)) { | 103 if (!alreadyWatched.contains(path)) { |
96 m_watcher.addPath(path); | 104 m_watcher.addPath(path); |
105 m_dirContents[path] = scanDirectory(path); | |
97 } | 106 } |
98 | 107 |
99 QDir d(path); | 108 QDir d(path); |
100 if (d.exists()) { | 109 if (d.exists()) { |
101 d.setFilter(QDir::Dirs | QDir::NoDotAndDotDot | | 110 d.setFilter(QDir::Dirs | QDir::NoDotAndDotDot | |
151 void | 160 void |
152 FsWatcher::fsDirectoryChanged(QString path) | 161 FsWatcher::fsDirectoryChanged(QString path) |
153 { | 162 { |
154 { | 163 { |
155 QMutexLocker locker(&m_mutex); | 164 QMutexLocker locker(&m_mutex); |
165 | |
156 if (shouldIgnore(path)) return; | 166 if (shouldIgnore(path)) return; |
157 size_t counter = ++m_lastCounter; | 167 |
158 m_changes[path] = counter; | 168 QSet<QString> files = scanDirectory(path); |
159 } | 169 if (files == m_dirContents[path]) { |
170 #ifdef DEBUG_FSWATCHER | |
171 std::cerr << "FsWatcher: Directory " << path << " has changed, but not in a way that we are monitoring" << std::endl; | |
172 #endif | |
173 return; | |
174 } | |
175 else m_dirContents[path] = files; | |
176 | |
177 size_t counter = ++m_lastCounter; | |
178 m_changes[path] = counter; | |
179 } | |
180 | |
160 emit changed(); | 181 emit changed(); |
161 } | 182 } |
162 | 183 |
163 void | 184 void |
164 FsWatcher::fsFileChanged(QString path) | 185 FsWatcher::fsFileChanged(QString path) |
165 { | 186 { |
166 QMutexLocker locker(&m_mutex); | 187 { |
188 QMutexLocker locker(&m_mutex); | |
189 | |
190 // We don't check whether the file matches an ignore pattern, | |
191 // because we are only notified for file changes if we are | |
192 // watching the file explicitly, i.e. the file is in the | |
193 // tracked file paths list. So we never want to ignore them | |
194 | |
195 size_t counter = ++m_lastCounter; | |
196 m_changes[path] = counter; | |
197 } | |
198 | |
199 emit changed(); | |
167 } | 200 } |
168 | 201 |
169 bool | 202 bool |
170 FsWatcher::shouldIgnore(QString path) | 203 FsWatcher::shouldIgnore(QString path) |
171 { | 204 { |
172 | 205 QFileInfo fi(path); |
173 } | 206 QString fn(fi.fileName()); |
174 | 207 foreach (QString pfx, m_ignoredPrefixes) { |
208 if (fn.startsWith(pfx)) return true; | |
209 } | |
210 foreach (QString sfx, m_ignoredSuffixes) { | |
211 if (fn.endsWith(sfx)) return true; | |
212 } | |
213 return false; | |
214 } | |
215 | |
216 QSet<QString> | |
217 FsWatcher::scanDirectory(QString path) | |
218 { | |
219 QSet<QString> files; | |
220 QDir d(path); | |
221 if (d.exists()) { | |
222 d.setFilter(QDir::Files | QDir::NoDotAndDotDot | | |
223 QDir::Readable | QDir::NoSymLinks); | |
224 foreach (QString entry, d.entryList()) { | |
225 if (entry.startsWith('.')) continue; | |
226 if (shouldIgnore(entry)) continue; | |
227 files.insert(entry); | |
228 } | |
229 } | |
230 return files; | |
231 } | |
232 | |
233 void | |
234 FsWatcher::debugPrint() | |
235 { | |
236 #ifdef DEBUG_FSWATCHER | |
237 std::cerr << "FsWatcher: Now watching " << m_watcher.directories().size() | |
238 << " directories and " << m_watcher.files().size() | |
239 << " files" << std::endl; | |
240 #endif | |
241 } |