Merge lp:~mhr3/unity-lens-applications/fix-842108 into lp:unity-lens-applications

Proposed by Michal Hruby
Status: Merged
Approved by: Didier Roche-Tolomelli
Approved revision: 285
Merged at revision: 285
Proposed branch: lp:~mhr3/unity-lens-applications/fix-842108
Merge into: lp:unity-lens-applications
Diff against target: 210 lines (+100/-42)
3 files modified
src/runner.vala (+87/-42)
tests/test-full-and-partial.sh (+7/-0)
tests/test-no-partial-commands.sh (+6/-0)
To merge this branch: bzr merge lp:~mhr3/unity-lens-applications/fix-842108
Reviewer Review Type Date Requested Status
Didier Roche-Tolomelli Approve
Review via email: mp+103617@code.launchpad.net

Commit message

Don't remove exact matches from list of suggestions

Description of the change

"Run command" lens doesn't include exact match when there are also partial matches.

Don't remove the exact match from list of suggestions and split out the searcher into a separate class to make it easier to unit test in the future.

Added unity-tool tests.

To post a comment you must log in.
Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

LGTM, thanks Michal!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/runner.vala'
2--- src/runner.vala 2012-02-22 15:14:36 +0000
3+++ src/runner.vala 2012-04-26 07:07:18 +0000
4@@ -50,8 +50,8 @@
5 public Unity.Scope scope;
6
7 private Gee.HashMap<string,AboutEntry> about_entries;
8- private Gee.List<string> executables;
9 private Gee.List<string> history;
10+ private ExecSearcher exec_searcher = new ExecSearcher ();
11
12 private Settings gp_settings;
13
14@@ -73,12 +73,6 @@
15 return lens_search.search_string.strip ();
16 });
17
18- /* Re-do the search if the filters changed */
19- scope.filters_changed.connect (() =>
20- {
21- scope.queue_search_changed (SearchType.DEFAULT);
22- });
23-
24 // Listen for when the lens is hidden/shown by the Unity Shell
25 scope.notify["active"].connect(() =>
26 {
27@@ -104,9 +98,6 @@
28 about_entries = new Gee.HashMap<string,AboutEntry> ();
29 load_about_entries ();
30
31- executables = new Gee.ArrayList<string> ();
32- find_system_executables.begin ();
33-
34 this.gp_settings = new Settings ("com.canonical.Unity.Runner");
35 history = new Gee.ArrayList<string> ();
36 load_history ();
37@@ -219,7 +210,7 @@
38 break;
39
40 var complete_path = Path.build_filename (search_dirname, subelem_info.get_name ());
41- if (complete_path.has_prefix (search_string) && complete_path != search_string)
42+ if (complete_path.has_prefix (search_string))
43 {
44 if (subelem_info.get_file_type () == FileType.DIRECTORY)
45 {
46@@ -244,12 +235,10 @@
47 /* complete again system executables */
48 else
49 {
50- foreach (var exec_candidate in this.executables)
51+ var matching_executables = yield exec_searcher.find_prefixed (search_string);
52+ foreach (var matching_exec in matching_executables)
53 {
54- if (exec_candidate.has_prefix (search_string) && exec_candidate != search_string)
55- {
56- executables_match.add (exec_candidate);
57- }
58+ executables_match.add (matching_exec);
59 }
60 }
61
62@@ -302,35 +291,91 @@
63 search.finished ();
64 }
65
66- // TODO: add reload
67- private async void find_system_executables ()
68+ private class ExecSearcher: Object
69 {
70-
71- if (this.executables.size > 0)
72- return;
73-
74- foreach (var path_directory in Environment.get_variable ("PATH").split(":"))
75- {
76- var dir = File.new_for_path (path_directory);
77- try {
78- var e = yield dir.enumerate_children_async (FILE_ATTRIBUTE_STANDARD_NAME + "," + FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
79- 0, Priority.DEFAULT, null);
80- while (true) {
81- var files = yield e.next_files_async (10, Priority.DEFAULT, null);
82- if (files == null)
83- break;
84-
85- foreach (var info in files) {
86- if (info.get_attribute_boolean (FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
87- {
88- this.executables.add (info.get_name ());
89+ public ExecSearcher ()
90+ {
91+ Object ();
92+ }
93+
94+ private Gee.List<string> executables;
95+
96+ construct
97+ {
98+ executables = new Gee.ArrayList<string> ();
99+ listing_status = ListingStatus.NOT_STARTED;
100+ }
101+
102+ // TODO: add reload
103+ private async void find_system_executables ()
104+ {
105+ if (this.executables.size > 0)
106+ return;
107+
108+ foreach (var path_directory in Environment.get_variable ("PATH").split(":"))
109+ {
110+ var dir = File.new_for_path (path_directory);
111+ try {
112+ var e = yield dir.enumerate_children_async (FILE_ATTRIBUTE_STANDARD_NAME + "," + FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
113+ 0, Priority.DEFAULT, null);
114+ while (true) {
115+ var files = yield e.next_files_async (64, Priority.DEFAULT, null);
116+ if (files == null)
117+ break;
118+
119+ foreach (var info in files) {
120+ if (info.get_attribute_boolean (FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
121+ {
122+ this.executables.add (info.get_name ());
123+ }
124 }
125 }
126 }
127- }
128- catch (Error err) {
129- warning("Error listing directory executables: %s\n", err.message);
130- }
131+ catch (Error err) {
132+ warning("Error listing directory executables: %s\n", err.message);
133+ }
134+ }
135+ }
136+
137+ private enum ListingStatus
138+ {
139+ NOT_STARTED,
140+ STARTED,
141+ FINISHED
142+ }
143+
144+ public int listing_status { get; private set; }
145+
146+ public async Gee.Collection<string> find_prefixed (string search_string)
147+ {
148+ // initialize the available binaries lazily
149+ if (listing_status == ListingStatus.NOT_STARTED)
150+ {
151+ listing_status = ListingStatus.STARTED;
152+ yield find_system_executables ();
153+ listing_status = ListingStatus.FINISHED;
154+ }
155+ else if (listing_status == ListingStatus.STARTED)
156+ {
157+ var sig_id = this.notify["listing-status"].connect (() =>
158+ {
159+ if (listing_status == ListingStatus.FINISHED)
160+ find_prefixed.callback ();
161+ });
162+ yield;
163+ SignalHandler.disconnect (this, sig_id);
164+ }
165+
166+ var matching = new Gee.ArrayList<string> ();
167+ foreach (var exec_candidate in executables)
168+ {
169+ if (exec_candidate.has_prefix (search_string))
170+ {
171+ matching.add (exec_candidate);
172+ }
173+ }
174+
175+ return matching;
176 }
177 }
178
179@@ -358,7 +403,7 @@
180 icon = ContentType.get_icon (mimetype);
181 return exec_string;
182 }
183-
184+
185 var s = exec_string.delimit ("-", '_').split (" ", 0)[0];
186 var appresults = this.daemon.appsearcher.search (@"type:Application AND exec:$s", 0,
187 Unity.Package.SearchType.EXACT,
188
189=== added file 'tests/test-full-and-partial.sh'
190--- tests/test-full-and-partial.sh 1970-01-01 00:00:00 +0000
191+++ tests/test-full-and-partial.sh 2012-04-26 07:07:18 +0000
192@@ -0,0 +1,7 @@
193+#!/bin/sh
194+
195+# Search for "ls", print the results, this search should only include both
196+# "ls", not "ls.+"
197+test `unity-tool -n com.canonical.Unity.Lens.Applications -p /com/canonical/unity/lens/commands -s "ls" --no-search-reply -r | grep "\\<ls\\>" | wc -l` -eq 1 || exit 1
198+test `unity-tool -n com.canonical.Unity.Lens.Applications -p /com/canonical/unity/lens/commands -s "ls" --no-search-reply -r | grep "\\<ls" | wc -l` -ge 2 || exit 1
199+
200
201=== added file 'tests/test-no-partial-commands.sh'
202--- tests/test-no-partial-commands.sh 1970-01-01 00:00:00 +0000
203+++ tests/test-no-partial-commands.sh 2012-04-26 07:07:18 +0000
204@@ -0,0 +1,6 @@
205+#!/bin/sh
206+
207+# Search for "modpro", print the results, this search should only include
208+# "modprobe", not "modpro" itself
209+test `unity-tool -n com.canonical.Unity.Lens.Applications -p /com/canonical/unity/lens/commands -s "modpro" --no-search-reply -r | grep "modpro" | wc -l` -eq `unity-tool -n com.canonical.Unity.Lens.Applications -p /com/canonical/unity/lens/commands -s "modpro" --no-search-reply -r | grep "modprobe" | wc -l` || exit 1
210+

Subscribers

People subscribed via source and target branches