Merge lp:~mhr3/unity-lens-files/with-locate into lp:unity-lens-files

Proposed by Michal Hruby
Status: Merged
Approved by: Didier Roche-Tolomelli
Approved revision: 218
Merged at revision: 213
Proposed branch: lp:~mhr3/unity-lens-files/with-locate
Merge into: lp:unity-lens-files
Diff against target: 459 lines (+247/-25)
9 files modified
.bzrignore (+4/-0)
configure.ac (+7/-0)
data/Makefile.am (+13/-1)
data/com.canonical.Unity.FilesLens.gschema.xml.in.in (+9/-0)
src/Makefile.am (+1/-0)
src/daemon.vala (+100/-11)
src/locate.vala (+82/-0)
src/utils.vala (+20/-13)
tests/manual/locate.txt (+11/-0)
To merge this branch: bzr merge lp:~mhr3/unity-lens-files/with-locate
Reviewer Review Type Date Requested Status
Didier Roche-Tolomelli Approve
Review via email: mp+95521@code.launchpad.net

Description of the change

Use locate to make sure we're able to find files which aren't logged by Zeitgeist. Also adds a gsettings schema where this can be easily disabled.

UNBLOCK

To post a comment you must log in.
Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

175 + /* We want to display something once dash is shown */
176 + scope.queue_search_changed (SearchType.GLOBAL);

I think this deserves a branch and bug report on its own.

240 + if (cancellable.is_cancelled ()) throw new IOError.CANCELLED ("Cancelled");

Can you add a more useful message, like "Cancelled locate search for '%s'" or something?

242 + var results = yield Locate.locate (query, cancellable);
243 + results.sort (Utils.cmp_file_info_by_mtime);

Should we not be checking cancellable before proceeding after the async call?

Otherwise, awesome work! It works quite well here on my sandybridge lappy. I wanna test this on my elcheapo netbook before I give it the final ack.

214. By Michal Hruby

Simplify cancellable usage

215. By Michal Hruby

Remove an unrelated fix, deserves its own branch

Revision history for this message
Michal Hruby (mhr3) wrote :

> 175 + /* We want to display something once dash is shown */
> 176 + scope.queue_search_changed (SearchType.GLOBAL);
>
> I think this deserves a branch and bug report on its own.

Fair enough. Removing from this branch.

>
>
> 240 + if (cancellable.is_cancelled ()) throw new IOError.CANCELLED
> ("Cancelled");
>
> Can you add a more useful message, like "Cancelled locate search for '%s'" or
> something?
>

Actually I should be just using cancellable.set_error_if_cancelled()

Moreover, daemon.vala:428:
      } catch (IOError.CANCELLED ioe) {
        return;

>
> 242 + var results = yield Locate.locate (query, cancellable);
> 243 + results.sort (Utils.cmp_file_info_by_mtime);
>
> Should we not be checking cancellable before proceeding after the async call?
>

No, an IOError is thrown in that case.

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

> > 242 + var results = yield Locate.locate (query, cancellable);
> > 243 + results.sort (Utils.cmp_file_info_by_mtime);
> >
> > Should we not be checking cancellable before proceeding after the async
> call?
> >
>
> No, an IOError is thrown in that case.

Not necessarily. If I understand GIO correctly it will raise G_IO_ERROR_CANCELLED if an async io call is cancelled. The problem is that you may also be cancelled when not in an IO call. In between the idles implied by the yields. Or...I have no idea how Vala async semantics intersects with exception handling...

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

I tested it out on my netbook and it was pretty smooth.

During testing I still couldn't help getting a creepy feeling that I was seeing an alarmingly low signal to noise ratio in the results. This is not a problem for people who can immediately recognize that they are seeing a bunch of system files, but for mortal users this must be most disconcerting.Doubly so because we have no contextual information on all of these system files.

I have a feeling that we should blacklist /usr /bin /lib /opt and /var.

The results of this tool on a multi user system is also pretty angst provoking...

Revision history for this message
Michal Hruby (mhr3) wrote :

> Not necessarily. If I understand GIO correctly it will raise G_IO_ERROR_CANCELLED if an ...

Sorted out on IRC.

> I tested it out on my netbook and it was pretty smooth.
>
> During testing I still couldn't help getting a creepy feeling that I was
> seeing an alarmingly low signal to noise ratio in the results. This is not a
> problem for people who can immediately recognize that they are seeing a bunch
> of system files, but for mortal users this must be most disconcerting.Doubly
> so because we have no contextual information on all of these system files.
>
> I have a feeling that we should blacklist /usr /bin /lib /opt and /var.
>
> The results of this tool on a multi user system is also pretty angst
> provoking...

Oh I remember we talked about filtering out of non-writable files, this branch isn't doing that yet, does anyone see any problems with adding it? (unfortunately it'll have to be post-locate filtering)

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

> > I have a feeling that we should blacklist /usr /bin /lib /opt and /var.
> >
> > The results of this tool on a multi user system is also pretty angst
> > provoking...
>
> Oh I remember we talked about filtering out of non-writable files, this branch
> isn't doing that yet, does anyone see any problems with adding it?
> (unfortunately it'll have to be post-locate filtering)

You're right forgot about that trick. I like that a lot better.

Revision history for this message
Sebastien Bacher (seb128) wrote :

couldn't you just limit the results to the user and /media,mnt? That would solve the "too much noise" and "other user files are listed"...

Revision history for this message
Sebastien Bacher (seb128) wrote :

to the user "directory"...

216. By Michal Hruby

Filter out backup files and files that are not writable

Revision history for this message
Michal Hruby (mhr3) wrote :

Pushed a version which filters out non-writable files, limiting the search to a couple of directories required using --regex for locate and that slowed it down far too much (up to 8 seconds for some queries on my system)

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

On 2 March 2012 17:12, Michal Hruby <email address hidden> wrote:
> Pushed a version which filters out non-writable files, limiting the search to a couple of directories required using --regex for locate and that slowed it down far too much (up to 8 seconds for some queries on my system)

I am seeing some confusing matching... Searching for 'config' gives me
6 hits while searching for 'config.' gives me 81 hits... Do you know
what gives?

Also, this kind of spurious matching behaviour will inevitably pop out
here and there, but that is a price we'll have to agree to pay...

217. By Michal Hruby

Add manual test for locate scope

218. By Michal Hruby

Make sure the manual-test performer uses touch (and didrocks will live happily ever after)

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

Ok, Michal ensured me that the technical concerns from Mikkel have been addressed. Now that there is a manual test for it (maybe some autopilot one day if we can run some command as root?) that's good for me (and it had a few days of testing already in the ubuntu-desktop ppa).

Nice to see a fix for that, well done :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2011-08-07 09:01:34 +0000
3+++ .bzrignore 2012-03-08 16:33:30 +0000
4@@ -1,6 +1,7 @@
5 src/config.c
6 src/daemon.c
7 src/folder.c
8+src/locate.c
9 src/main.c
10 src/schemas.c
11 src/url-checker.c
12@@ -43,6 +44,9 @@
13 data/Makefile
14 data/Makefile.in
15 data/unity-lens-files.service
16+data/com.canonical.Unity.FilesLens.gschema.valid
17+data/com.canonical.Unity.FilesLens.gschema.xml
18+data/com.canonical.Unity.FilesLens.gschema.xml.in
19 src/Makefile
20 src/Makefile.in
21 src/config.vala
22
23=== modified file 'configure.ac'
24--- configure.ac 2012-02-17 10:43:57 +0000
25+++ configure.ac 2012-03-08 16:33:30 +0000
26@@ -100,6 +100,12 @@
27 fi
28 AC_SUBST(DBUSSERVICEDIR)
29
30+#############################################
31+# GSettings macros
32+#############################################
33+
34+GLIB_GSETTINGS
35+
36 #####################################################
37 # Look for correct Lenses dir
38 #####################################################
39@@ -117,6 +123,7 @@
40 Makefile
41 files.lens.in
42 data/Makefile
43+ data/com.canonical.Unity.FilesLens.gschema.xml.in
44 src/Makefile
45 src/config.vala
46 po/Makefile.in
47
48=== modified file 'data/Makefile.am'
49--- data/Makefile.am 2011-08-04 14:15:30 +0000
50+++ data/Makefile.am 2012-03-08 16:33:30 +0000
51@@ -5,8 +5,20 @@
52 %.service: %.service.in
53 sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
54
55+#############################################################
56+# GSettings schemas
57+#############################################################
58+gsettings_SCHEMAS = com.canonical.Unity.FilesLens.gschema.xml
59+
60+@INTLTOOL_XML_NOMERGE_RULE@
61+
62+@GSETTINGS_RULES@
63+
64 EXTRA_DIST = \
65+ com.canonical.Unity.FilesLens.gschema.xml.in.in \
66 $(service_in_files)
67
68 CLEANFILES = \
69- unity-lens-files.service
70+ unity-lens-files.service \
71+ $(gsettings_SCHEMAS)
72+
73
74=== added file 'data/com.canonical.Unity.FilesLens.gschema.xml.in.in'
75--- data/com.canonical.Unity.FilesLens.gschema.xml.in.in 1970-01-01 00:00:00 +0000
76+++ data/com.canonical.Unity.FilesLens.gschema.xml.in.in 2012-03-08 16:33:30 +0000
77@@ -0,0 +1,9 @@
78+<schemalist>
79+ <schema path="/desktop/unity/lenses/files/" id="com.canonical.Unity.FilesLens" gettext-domain="unity-lens-files">
80+ <key type="b" name="use-locate">
81+ <default>true</default>
82+ <summary>Use locate during searches.</summary>
83+ <description>Use locate during searches to make sure the lens is able to find most of their files even it they're not logged by Zeitgeist.</description>
84+ </key>
85+ </schema>
86+</schemalist>
87
88=== modified file 'src/Makefile.am'
89--- src/Makefile.am 2011-09-29 10:19:09 +0000
90+++ src/Makefile.am 2012-03-08 16:33:30 +0000
91@@ -39,6 +39,7 @@
92 config.vala \
93 daemon.vala \
94 folder.vala \
95+ locate.vala \
96 main.vala \
97 schemas.vala \
98 url-checker.vala \
99
100=== modified file 'src/daemon.vala'
101--- src/daemon.vala 2012-02-14 13:44:37 +0000
102+++ src/daemon.vala 2012-03-08 16:33:30 +0000
103@@ -1,5 +1,5 @@
104 /*
105- * Copyright (C) 2010 Canonical Ltd
106+ * Copyright (C) 2010-2012 Canonical Ltd
107 *
108 * This program is free software: you can redistribute it and/or modify
109 * it under the terms of the GNU General Public License version 3 as
110@@ -14,6 +14,7 @@
111 * along with this program. If not, see <http://www.gnu.org/licenses/>.
112 *
113 * Authored by Mikkel Kamstrup Erlandsen <mikkel.kamstrup@canonical.com>
114+ * Michal Hruby <michal.hruby@canonical.com>
115 *
116 */
117 using Zeitgeist;
118@@ -41,9 +42,22 @@
119 * we use to query Zeitgeist */
120 private HashTable<string, Event> type_templates;
121
122+ private const string SCHEMA_NAME = "com.canonical.Unity.FilesLens";
123+ private const string USE_LOCATE_KEY = "use-locate";
124+ public bool use_locate { get; set; default = true; }
125+
126+ private Settings lens_settings;
127+
128 construct
129 {
130- prepare_type_templates();
131+ prepare_type_templates ();
132+
133+ if (SCHEMA_NAME in Settings.list_schemas ())
134+ {
135+ lens_settings = new Settings (SCHEMA_NAME);
136+ lens_settings.bind (USE_LOCATE_KEY, this, USE_LOCATE_KEY,
137+ SettingsBindFlags.GET);
138+ }
139
140 scope = new Unity.Scope ("/com/canonical/unity/scope/files");
141 scope.search_in_global = true;
142@@ -54,15 +68,15 @@
143 lens.search_hint = _("Search Files & Folders");
144 lens.visible = true;
145 populate_categories ();
146- populate_filters();
147+ populate_filters ();
148 lens.add_local_scope (scope);
149
150 /* Bring up Zeitgeist interfaces */
151- log = new Zeitgeist.Log();
152- index = new Zeitgeist.Index();
153+ log = new Zeitgeist.Log ();
154+ index = new Zeitgeist.Index ();
155
156 /* Listen for all file:// related events from Zeitgeist */
157- var templates = new PtrArray();
158+ var templates = new PtrArray ();
159 var event = new Zeitgeist.Event ();
160 var subject = new Zeitgeist.Subject ();
161 subject.set_uri ("file://*");
162@@ -73,7 +87,7 @@
163 monitor.events_inserted.connect (on_zeitgeist_changed);
164 monitor.events_deleted.connect (on_zeitgeist_changed);
165 log.install_monitor (monitor);
166-
167+
168 bookmarks = new Bookmarks ();
169 urls = new UrlChecker ();
170
171@@ -400,6 +414,14 @@
172 search.search_string);
173 }
174
175+ if (has_search && use_locate)
176+ {
177+ yield perform_locate (500, search.search_string,
178+ search.results_model, cancellable,
179+ Categories.FILES_AND_FOLDERS,
180+ Categories.FILES_AND_FOLDERS);
181+ }
182+
183 } catch (IOError.CANCELLED ioe) {
184 return;
185 } catch (GLib.Error e) {
186@@ -504,6 +526,12 @@
187 if (!txn.is_committed ()) txn.commit ();
188 }
189
190+ if (has_search && use_locate)
191+ {
192+ yield perform_locate (500, search.search_string,
193+ search.results_model, cancellable);
194+ }
195+
196 } catch (IOError.CANCELLED ioe) {
197 return;
198 } catch (GLib.Error e) {
199@@ -710,9 +738,8 @@
200 return;
201 }
202
203- if (cancellable.is_cancelled ())
204- throw new IOError.CANCELLED ("Search was cancelled");
205-
206+ cancellable.set_error_if_cancelled (); // throws IOError
207+
208 /* Sort files by mtime, we do an ugly nested ternary
209 * to avoid potential long/int overflow */
210 downloads.sort ((CompareFunc) Utils.cmp_file_info_by_mtime);
211@@ -720,6 +747,7 @@
212 var timerange = get_current_timerange ();
213 int64 min_size, max_size;
214 get_current_size_limits (out min_size, out max_size);
215+ var types = get_current_types ();
216
217 foreach (var info in downloads)
218 {
219@@ -734,7 +762,6 @@
220 continue;
221
222 // check if type matches
223- var types = get_current_types ();
224 if (types != null && !Utils.file_info_matches_any (info, types))
225 continue;
226
227@@ -753,6 +780,68 @@
228 }
229 }
230
231+ private async void perform_locate (uint timeout,
232+ string query,
233+ Dee.Model results_model,
234+ Cancellable cancellable,
235+ int files_category = Categories.RECENT,
236+ int dirs_category = Categories.FOLDERS)
237+ throws Error
238+ {
239+ /* Wait a while before starting the locate search */
240+ Timeout.add_full (Priority.LOW, timeout, perform_locate.callback);
241+ yield;
242+ cancellable.set_error_if_cancelled (); // throws IOError
243+
244+ var results = yield Locate.locate (query, cancellable);
245+ results.sort (Utils.cmp_file_info_by_mtime);
246+
247+ /* make a list of uris we already have */
248+ var present_uris = new Gee.HashSet<string> ();
249+ var model_iter = results_model.get_first_iter ();
250+ var model_end_iter = results_model.get_last_iter ();
251+ while (model_iter != model_end_iter)
252+ {
253+ present_uris.add (results_model.get_string (model_iter, 0));
254+ model_iter = results_model.next (model_iter);
255+ }
256+
257+ var timerange = get_current_timerange ();
258+ int64 min_size, max_size;
259+ get_current_size_limits (out min_size, out max_size);
260+ var types = get_current_types ();
261+
262+ foreach (var info in results)
263+ {
264+ var uri = info.get_data<File> ("associated-gfile").get_uri ();
265+ warn_if_fail (uri != null);
266+ if (uri == null || uri in present_uris) continue;
267+ var mimetype = info.get_attribute_string (FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
268+ var icon_hint = Utils.check_icon_string (uri, mimetype, info);
269+
270+ // check if we match the timerange
271+ uint64 mtime = info.get_attribute_uint64 (FILE_ATTRIBUTE_TIME_MODIFIED) * 1000;
272+
273+ if (mtime < timerange.get_start() || mtime > timerange.get_end ())
274+ continue;
275+
276+ // check if type matches
277+ if (types != null && !Utils.file_info_matches_any (info, types))
278+ continue;
279+
280+ // check if size is within bounds
281+ int64 size = info.get_size ();
282+ if (size < min_size || size > max_size)
283+ continue;
284+
285+ uint category_id = mimetype == "inode/directory" ?
286+ dirs_category : files_category;
287+
288+ results_model.append (uri, icon_hint, category_id,
289+ mimetype, info.get_display_name (), uri);
290+ }
291+ }
292+
293 public Unity.ActivationResponse activate (string uri)
294 {
295 debug (@"Activating: $uri");
296
297=== added file 'src/locate.vala'
298--- src/locate.vala 1970-01-01 00:00:00 +0000
299+++ src/locate.vala 2012-03-08 16:33:30 +0000
300@@ -0,0 +1,82 @@
301+/*
302+ * Copyright (C) 2010-2012 Canonical Ltd
303+ *
304+ * This program is free software: you can redistribute it and/or modify
305+ * it under the terms of the GNU General Public License version 3 as
306+ * published by the Free Software Foundation.
307+ *
308+ * This program is distributed in the hope that it will be useful,
309+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
310+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
311+ * GNU General Public License for more details.
312+ *
313+ * You should have received a copy of the GNU General Public License
314+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
315+ *
316+ * Authored by Michal Hruby <michal.hruby@canonical.com>
317+ *
318+ */
319+using Zeitgeist;
320+using Config;
321+using Gee;
322+
323+namespace Unity.FilesLens.Locate
324+{
325+ const uint MAX_RESULTS = 128;
326+
327+ /* Careful about usage of FILE_ATTRIBUTE_STANDARD_ICON, this is the same as
328+ * requesting FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE and causes reading
329+ * of the target file, we just want to stat it */
330+ const string ATTRIBS = FILE_ATTRIBUTE_STANDARD_IS_HIDDEN + "," +
331+ FILE_ATTRIBUTE_STANDARD_IS_BACKUP + "," +
332+ FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME + "," +
333+ FILE_ATTRIBUTE_STANDARD_SIZE + "," +
334+ FILE_ATTRIBUTE_TIME_MODIFIED + "," +
335+ FILE_ATTRIBUTE_THUMBNAIL_PATH + "," +
336+ FILE_ATTRIBUTE_ACCESS_CAN_WRITE + "," +
337+ FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE;
338+
339+ public async GLib.SList<FileInfo> locate (string input_query,
340+ Cancellable cancellable) throws Error
341+ {
342+ var result = new GLib.SList<FileInfo> ();
343+ var query = Regex.escape_string (input_query.strip ());
344+
345+ // FIXME: we could limit the search to specific directories, but using regex
346+ // matching slows down the search considerably
347+ string[] argv = { "locate", "-i", "-e", "-l", "%u".printf (MAX_RESULTS),
348+ "*%s*".printf (query.replace (" ", "*")) };
349+
350+ Pid pid;
351+ int read_fd;
352+ string? line = null;
353+
354+ Process.spawn_async_with_pipes (null, argv, null, SpawnFlags.SEARCH_PATH,
355+ null, out pid, null, out read_fd);
356+
357+ UnixInputStream read_stream = new UnixInputStream (read_fd, true);
358+ DataInputStream locate_output = new DataInputStream (read_stream);
359+
360+ do
361+ {
362+ line = yield locate_output.read_line_async (Priority.DEFAULT_IDLE,
363+ cancellable);
364+ if (line == null) break;
365+ if ("/." in line) continue; // we don't want no hidden files
366+
367+ var file = File.new_for_path (line);
368+ var fi = yield file.query_info_async (ATTRIBS, 0, Priority.DEFAULT_IDLE,
369+ cancellable);
370+ if (fi.get_is_hidden () || fi.get_is_backup () ||
371+ !fi.get_attribute_boolean (FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) continue;
372+ fi.set_data ("associated-gfile", file);
373+ result.prepend (fi);
374+
375+ } while (line != null);
376+
377+ result.reverse ();
378+ return result;
379+ }
380+
381+} /* namespace */
382+
383
384=== modified file 'src/utils.vala'
385--- src/utils.vala 2012-01-27 17:14:56 +0000
386+++ src/utils.vala 2012-03-08 16:33:30 +0000
387@@ -85,23 +85,25 @@
388
389 public string check_icon_string (string uri, string mimetype, FileInfo info)
390 {
391- Icon icon = info.get_icon();
392+ Icon icon = info.get_icon ();
393 string thumbnail_path = info.get_attribute_byte_string (FILE_ATTRIBUTE_THUMBNAIL_PATH);
394
395- if (thumbnail_path == null)
396- return icon.to_string();
397+ if (thumbnail_path == null && icon != null)
398+ return icon.to_string ();
399+ else if (icon == null)
400+ return ContentType.get_icon (mimetype).to_string ();
401 else
402- return new FileIcon(File.new_for_path (thumbnail_path)).to_string ();
403+ return new FileIcon (File.new_for_path (thumbnail_path)).to_string ();
404 }
405
406- public int cmp_file_info_by_mtime (FileInfo info1, FileInfo info2)
407- {
408- TimeVal tv1, tv2;
409- info1.get_modification_time (out tv1);
410- info2.get_modification_time (out tv2);
411- long cmp = tv1.tv_sec - tv2.tv_sec;
412- return cmp < 0 ? 1 : ( cmp > 0 ? -1 : 0);
413- }
414+ public int cmp_file_info_by_mtime (FileInfo info1, FileInfo info2)
415+ {
416+ TimeVal tv1, tv2;
417+ info1.get_modification_time (out tv1);
418+ info2.get_modification_time (out tv2);
419+ long cmp = tv1.tv_sec - tv2.tv_sec;
420+ return cmp < 0 ? 1 : ( cmp > 0 ? -1 : 0);
421+ }
422
423 public string get_month_name (DateMonth month)
424 {
425@@ -333,10 +335,15 @@
426
427 public bool file_info_matches_type (FileInfo info, string type_id)
428 {
429+ unowned string interpretation;
430 unowned string content_type = info.get_content_type ();
431- unowned string interpretation;
432+ if (content_type == null)
433+ content_type = info.get_attribute_string (FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
434+
435+ if (content_type == null) content_type = "application/octet-stream";
436
437 interpretation = Zeitgeist.interpretation_for_mimetype (content_type);
438+ if (interpretation == null) return type_id == "other" || type_id == "all";
439
440 /* type_id is one of: documents, folders, images, audio, videos,
441 * presentations, other */
442
443=== added directory 'tests'
444=== added directory 'tests/manual'
445=== added file 'tests/manual/locate.txt'
446--- tests/manual/locate.txt 1970-01-01 00:00:00 +0000
447+++ tests/manual/locate.txt 2012-03-08 16:33:30 +0000
448@@ -0,0 +1,11 @@
449+Locate scope
450+============
451+Tests that the locate scope works.
452+
453+#. Create a file named "report.txt" in your home directory (`touch report.txt`).
454+#. Run updatedb (`sudo updatedb`).
455+#. Search for this file with files lens.
456+
457+Outcome
458+ The "report.txt" should show up as a result in the Recent category of files lens.
459+

Subscribers

People subscribed via source and target branches