Merge lp:~desrt/indicator-appmenu/hud-performance into lp:indicator-appmenu/0.4
- hud-performance
- Merge into trunk.0.4
Proposed by
Allison Lortie
on 2012-04-02
| Status: | Merged | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| Approved by: | Charles Kerr on 2012-04-03 | ||||||||
| Approved revision: | 213 | ||||||||
| Merged at revision: | 197 | ||||||||
| Proposed branch: | lp:~desrt/indicator-appmenu/hud-performance | ||||||||
| Merge into: | lp:indicator-appmenu/0.4 | ||||||||
| Diff against target: |
2576 lines (+1367/-705) 34 files modified
configure.ac (+1/-1) data/Makefile.am (+5/-13) data/com.canonical.indicator.appmenu.gschema.xml (+18/-0) data/com.canonical.indicator.appmenu.gschema.xml.in (+0/-15) data/com.canonical.indicator.appmenu.hud.gschema.xml (+16/-0) data/com.canonical.indicator.appmenu.hud.gschema.xml.in (+0/-14) data/com.canonical.indicator.appmenu.hud.search.gschema.xml (+72/-0) data/com.canonical.indicator.appmenu.hud.search.gschema.xml.in (+0/-90) po/POTFILES.in (+4/-4) src/Makefile.am (+2/-2) src/distance.c (+0/-415) src/distance.h (+0/-33) src/hudappindicatorsource.c (+4/-4) src/huddbusmenucollector.c (+3/-3) src/huddebugsource.c (+3/-3) src/hudindicatorsource.c (+3/-3) src/huditem.c (+11/-0) src/huditem.h (+2/-1) src/hudmenumodelcollector.c (+3/-3) src/hudquery.c (+9/-1) src/hudresult.c (+13/-40) src/hudresult.h (+12/-12) src/hudsettings.c (+4/-15) src/hudsettings.h (+1/-4) src/hudsource.c (+3/-3) src/hudsource.h (+13/-13) src/hudsourcelist.c (+3/-3) src/hudtoken.c (+394/-0) src/hudtoken.h (+40/-0) src/hudwindowsource.c (+9/-4) tests/Makefile.am (+18/-0) tests/hud-performance.c (+279/-0) tests/test-distance.c (+58/-6) tests/word-list.h (+364/-0) |
||||||||
| To merge this branch: | bzr merge lp:~desrt/indicator-appmenu/hud-performance | ||||||||
| Related bugs: |
|
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Ted Gould (community) | 2012-04-02 | Approve on 2012-04-02 | |
|
Review via email:
|
|||
Commit Message
Description of the Change
Improve hud-service performance and search result quality by substantially reworking the core distance algorithm and the needle/haystack assignment algorithm.
To post a comment you must log in.
lp:~desrt/indicator-appmenu/hud-performance
updated
on 2012-04-03
- 212. By Allison Lortie on 2012-04-03
-
Adjustments for Ted's review comments.
- 213. By Allison Lortie on 2012-04-03
-
HudWindowSource: fix a couple of leaks
- don't leak icon name while changing windows (fixes lp:972354)
- don't leak the collector on finalize (which never happens)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
| 1 | === modified file 'configure.ac' |
| 2 | --- configure.ac 2012-03-28 23:37:14 +0000 |
| 3 | +++ configure.ac 2012-04-03 12:30:26 +0000 |
| 4 | @@ -222,7 +222,7 @@ |
| 5 | |
| 6 | GETTEXT_PACKAGE=indicator-appmenu |
| 7 | |
| 8 | -IT_PROG_INTLTOOL([0.41.0]) |
| 9 | +IT_PROG_INTLTOOL([0.50.2]) |
| 10 | |
| 11 | AM_GNU_GETTEXT([external]) |
| 12 | AM_GNU_GETTEXT_VERSION([0.17]) |
| 13 | |
| 14 | === modified file 'data/Makefile.am' |
| 15 | --- data/Makefile.am 2012-01-24 14:24:30 +0000 |
| 16 | +++ data/Makefile.am 2012-04-03 12:30:26 +0000 |
| 17 | @@ -1,13 +1,8 @@ |
| 18 | - |
| 19 | -gsettings_in_files = \ |
| 20 | - com.canonical.indicator.appmenu.gschema.xml.in \ |
| 21 | - com.canonical.indicator.appmenu.hud.gschema.xml.in \ |
| 22 | - com.canonical.indicator.appmenu.hud.search.gschema.xml.in |
| 23 | - |
| 24 | gsettings_SCHEMAS = \ |
| 25 | - $(gsettings_in_files:.xml.in=.xml) |
| 26 | + com.canonical.indicator.appmenu.gschema.xml \ |
| 27 | + com.canonical.indicator.appmenu.hud.gschema.xml \ |
| 28 | + com.canonical.indicator.appmenu.hud.search.gschema.xml |
| 29 | |
| 30 | -@INTLTOOL_XML_NOMERGE_RULE@ |
| 31 | @GSETTINGS_RULES@ |
| 32 | |
| 33 | dbus_servicesdir = $(DBUSSERVICEDIR) |
| 34 | @@ -18,10 +13,7 @@ |
| 35 | |
| 36 | EXTRA_DIST = \ |
| 37 | $(dbus_services_DATA:.service=.service.in) \ |
| 38 | - $(gsettings_in_files) |
| 39 | + $(gsettings_SCHEMAS) |
| 40 | |
| 41 | CLEANFILES = \ |
| 42 | - $(dbus_services_DATA) \ |
| 43 | - $(gsettings_SCHEMAS:.xml=.valid) \ |
| 44 | - $(gsettings_SCHEMAS) |
| 45 | - |
| 46 | + $(dbus_services_DATA) |
| 47 | |
| 48 | === added file 'data/com.canonical.indicator.appmenu.gschema.xml' |
| 49 | --- data/com.canonical.indicator.appmenu.gschema.xml 1970-01-01 00:00:00 +0000 |
| 50 | +++ data/com.canonical.indicator.appmenu.gschema.xml 2012-04-03 12:30:26 +0000 |
| 51 | @@ -0,0 +1,18 @@ |
| 52 | +<?xml version='1.0' encoding='UTF-8'?> |
| 53 | + |
| 54 | +<schemalist> |
| 55 | + <enum id='menu-enum'> |
| 56 | + <value value='0' nick='global'/> |
| 57 | + <value value='1' nick='locally-integrated'/> |
| 58 | + </enum> |
| 59 | + |
| 60 | + <schema path='/com/canonical/indicator/appmenu/' id='com.canonical.indicator.appmenu' gettext-domain='indicator-appmenu'> |
| 61 | + <key name='menu-mode' enum='menu-enum'> |
| 62 | + <default>'global'</default> |
| 63 | + <summary>Where the menus displayed</summary> |
| 64 | + <description> |
| 65 | + Controls the menu display location. TODO: add more |
| 66 | + </description> |
| 67 | + </key> |
| 68 | + </schema> |
| 69 | +</schemalist> |
| 70 | |
| 71 | === removed file 'data/com.canonical.indicator.appmenu.gschema.xml.in' |
| 72 | --- data/com.canonical.indicator.appmenu.gschema.xml.in 2012-01-17 17:38:12 +0000 |
| 73 | +++ data/com.canonical.indicator.appmenu.gschema.xml.in 1970-01-01 00:00:00 +0000 |
| 74 | @@ -1,15 +0,0 @@ |
| 75 | -<schemalist> |
| 76 | - <enum id="menu-enum"> |
| 77 | - <value nick="global" value="0" /> |
| 78 | - <value nick="locally-integrated" value="1" /> |
| 79 | - </enum> |
| 80 | - <schema id="com.canonical.indicator.appmenu" path="/com/canonical/indicator/appmenu/" gettext-domain="indicator-appmenu"> |
| 81 | - <key name="menu-mode" enum="menu-enum"> |
| 82 | - <default>'global'</default> |
| 83 | - <_summary>Where the menus displayed</_summary> |
| 84 | - <_description> |
| 85 | - Controls the menu display location. TODO: add more |
| 86 | - </_description> |
| 87 | - </key> |
| 88 | - </schema> |
| 89 | -</schemalist> |
| 90 | |
| 91 | === added file 'data/com.canonical.indicator.appmenu.hud.gschema.xml' |
| 92 | --- data/com.canonical.indicator.appmenu.hud.gschema.xml 1970-01-01 00:00:00 +0000 |
| 93 | +++ data/com.canonical.indicator.appmenu.hud.gschema.xml 2012-04-03 12:30:26 +0000 |
| 94 | @@ -0,0 +1,16 @@ |
| 95 | +<?xml version='1.0' encoding='UTF-8'?> |
| 96 | + |
| 97 | +<schemalist> |
| 98 | + <schema path='/com/canonical/indicator/appmenu/hud/' id='com.canonical.indicator.appmenu.hud' gettext-domain='indicator-appmenu'> |
| 99 | + <key type='b' name='store-usage-data'> |
| 100 | + <default>true</default> |
| 101 | + <summary>Whether to store usage data</summary> |
| 102 | + <description> |
| 103 | + When the HUD executes operations it stores the execution in order to |
| 104 | + make the future results better. Some users could choose to not want |
| 105 | + this data to be stored. If that is the case they should disable this |
| 106 | + property. |
| 107 | + </description> |
| 108 | + </key> |
| 109 | + </schema> |
| 110 | +</schemalist> |
| 111 | |
| 112 | === removed file 'data/com.canonical.indicator.appmenu.hud.gschema.xml.in' |
| 113 | --- data/com.canonical.indicator.appmenu.hud.gschema.xml.in 2012-01-17 17:38:12 +0000 |
| 114 | +++ data/com.canonical.indicator.appmenu.hud.gschema.xml.in 1970-01-01 00:00:00 +0000 |
| 115 | @@ -1,14 +0,0 @@ |
| 116 | -<schemalist> |
| 117 | - <schema id="com.canonical.indicator.appmenu.hud" path="/com/canonical/indicator/appmenu/hud/" gettext-domain="indicator-appmenu"> |
| 118 | - <key name="store-usage-data" type="b"> |
| 119 | - <default>true</default> |
| 120 | - <_summary>Whether to store usage data</_summary> |
| 121 | - <_description> |
| 122 | - When the HUD executes operations it stores the execution in |
| 123 | - order to make the future results better. Some users could choose |
| 124 | - to not want this data to be stored. If that is the case they |
| 125 | - should disable this property. |
| 126 | - </_description> |
| 127 | - </key> |
| 128 | - </schema> |
| 129 | -</schemalist> |
| 130 | |
| 131 | === added file 'data/com.canonical.indicator.appmenu.hud.search.gschema.xml' |
| 132 | --- data/com.canonical.indicator.appmenu.hud.search.gschema.xml 1970-01-01 00:00:00 +0000 |
| 133 | +++ data/com.canonical.indicator.appmenu.hud.search.gschema.xml 2012-04-03 12:30:26 +0000 |
| 134 | @@ -0,0 +1,72 @@ |
| 135 | +<?xml version='1.0' encoding='UTF-8'?> |
| 136 | + |
| 137 | +<schemalist> |
| 138 | + <schema path='/com/canonical/indicator/appmenu/hud/search/' |
| 139 | + id='com.canonical.indicator.appmenu.hud.search' |
| 140 | + gettext-domain='indicator-appmenu'> |
| 141 | + <key type='u' name='indicator-penalty'> |
| 142 | + <default>50</default> |
| 143 | + <summary>The penalty given to a menu item being in an indicator</summary> |
| 144 | + <description> |
| 145 | + In order to have the application's menu items appear higher in the search results a slight penalty is |
| 146 | + given to the indicator menu items. This value represents the percentage of that penalty so a value of |
| 147 | + '50' is a 50% additional to the calculated distance. |
| 148 | + </description> |
| 149 | + </key> |
| 150 | + |
| 151 | + <key type='u' name='add-penalty'> |
| 152 | + <default>10</default> |
| 153 | + <summary>Penalty for extra characters added to the search</summary> |
| 154 | + <description> |
| 155 | + The penalty for each extra character in the search string that does not appear in the text of a menu |
| 156 | + item. |
| 157 | + |
| 158 | + This penalty would be applied if the user typed "fiile" when searching against the menu item "File", for |
| 159 | + example. |
| 160 | + </description> |
| 161 | + </key> |
| 162 | + |
| 163 | + <key type='u' name='drop-penalty'> |
| 164 | + <default>10</default> |
| 165 | + <summary>Penalty applied if a character is dropped</summary> |
| 166 | + <description> |
| 167 | + The penalty for each character dropped from the search string, as compared with the text of a menu item. |
| 168 | + This only applies to missing characters that are not at the end of the search term. |
| 169 | + |
| 170 | + This penalty would be applied if the user typed "fle" when searching against the menu item "File", for |
| 171 | + example. |
| 172 | + </description> |
| 173 | + </key> |
| 174 | + |
| 175 | + <key type='u' name='end-drop-penalty'> |
| 176 | + <default>1</default> |
| 177 | + <summary>Penalty applied if a character is dropped from the end</summary> |
| 178 | + <description> |
| 179 | + The penalty for each missing character at the end of a search term. |
| 180 | + |
| 181 | + This penalty would be applied if the user typed "fil" when searching against the menu item "File", for |
| 182 | + example. |
| 183 | + </description> |
| 184 | + </key> |
| 185 | + |
| 186 | + <key type='u' name='swap-penalty'> |
| 187 | + <default>15</default> |
| 188 | + <summary>Penalty applied when the characters are not the same</summary> |
| 189 | + <description> |
| 190 | + The penalty for each substituted character in the search term. |
| 191 | + |
| 192 | + The penalty would be applied if the user typed "fike" when searching against the menu item "File", for |
| 193 | + example. |
| 194 | + </description> |
| 195 | + </key> |
| 196 | + |
| 197 | + <key type='u' name='max-distance'> |
| 198 | + <default>30</default> |
| 199 | + <summary>The highest distance value that is shown in the results</summary> |
| 200 | + <description> |
| 201 | + After the distances are calculated (including the indicator penalty) then all values above this max are |
| 202 | + dropped. This means that the history for those entries aren't looked up as well. |
| 203 | + </description> |
| 204 | + </key> |
| 205 | + </schema> |
| 206 | +</schemalist> |
| 207 | |
| 208 | === removed file 'data/com.canonical.indicator.appmenu.hud.search.gschema.xml.in' |
| 209 | --- data/com.canonical.indicator.appmenu.hud.search.gschema.xml.in 2012-03-03 13:39:53 +0000 |
| 210 | +++ data/com.canonical.indicator.appmenu.hud.search.gschema.xml.in 1970-01-01 00:00:00 +0000 |
| 211 | @@ -1,90 +0,0 @@ |
| 212 | -<schemalist> |
| 213 | - <schema id="com.canonical.indicator.appmenu.hud.search" path="/com/canonical/indicator/appmenu/hud/search/" gettext-domain="indicator-appmenu"> |
| 214 | - <key name="indicator-penalty" type="u"> |
| 215 | - <default>50</default> |
| 216 | - <_summary>The penalty given to a menu item being in an indicator</_summary> |
| 217 | - <_description> |
| 218 | - In order to have the application's menu items appear higher |
| 219 | - in the search results a slight penalty is given to the indicator |
| 220 | - menu items. This value represents then percentage of that penalty |
| 221 | - so a value of '50' is a 50% additional to the calculated distance. |
| 222 | - </_description> |
| 223 | - </key> |
| 224 | - <key name="add-penalty" type="u"> |
| 225 | - <default>10</default> |
| 226 | - <_summary>Penalty for extra characters added to the search</_summary> |
| 227 | - <_description> |
| 228 | - Penalty applied for extra characters that are added on the end of |
| 229 | - the string that are not related to the source string. |
| 230 | - </_description> |
| 231 | - </key> |
| 232 | - <key name="add-penalty-pre" type="u"> |
| 233 | - <default>1</default> |
| 234 | - <_summary>Penalty for extra characters added to the search before the length of the search string</_summary> |
| 235 | - <_description> |
| 236 | - Extra characters that occur before the length of the search string |
| 237 | - could cover them get this penalty. In the case of "return" and |
| 238 | - a search string of "turn" the 'r' and the 'e' would get hit with |
| 239 | - this penalty. |
| 240 | - </_description> |
| 241 | - </key> |
| 242 | - <key name="drop-penalty" type="u"> |
| 243 | - <default>10</default> |
| 244 | - <_summary>Penalty applied if a character is dropped</_summary> |
| 245 | - <_description> |
| 246 | - The penalty applied for characters that were dropped off the end |
| 247 | - of the search string compared to the tested string. This is mostly |
| 248 | - used for seeding and creating a max gradient on the edge of the |
| 249 | - matrix, as mostly dropped single characters are covered by the |
| 250 | - 'drop-penalty-end' value. |
| 251 | - </_description> |
| 252 | - </key> |
| 253 | - <key name="drop-penalty-end" type="u"> |
| 254 | - <default>10</default> |
| 255 | - <_summary>Penalty applied if a character is dropped after the search string length</_summary> |
| 256 | - <_description> |
| 257 | - Drop penalty that is added to characters that are longer than the |
| 258 | - current search string. So for a search string of "add" and a source |
| 259 | - string of "added" there would be drop end penalties for both 'e' and |
| 260 | - 'd'. |
| 261 | - </_description> |
| 262 | - </key> |
| 263 | - <key name="transpose-penalty" type="u"> |
| 264 | - <default>10</default> |
| 265 | - <_summary>Penalty applied when characters are transposed</_summary> |
| 266 | - <_description> |
| 267 | - When two characters are transposed this penalty is applied. This is |
| 268 | - for values like "ea" when the correct value is "ae". This penalty should |
| 269 | - be less than twice the swap penalty to have any effect. |
| 270 | - </_description> |
| 271 | - </key> |
| 272 | - <key name="swap-penalty" type="u"> |
| 273 | - <default>10</default> |
| 274 | - <_summary>Penalty applied when the characters are not the same</_summary> |
| 275 | - <_description> |
| 276 | - When two characters don't match at all this is the penalty |
| 277 | - that is given. This should probably be the highest penalty |
| 278 | - in the set of all the values. |
| 279 | - </_description> |
| 280 | - </key> |
| 281 | - <key name="swap-penalty-case" type="u"> |
| 282 | - <default>1</default> |
| 283 | - <_summary>The swap penalty when the swap is a case change</_summary> |
| 284 | - <_description> |
| 285 | - While not strictly a swap, this value allows for penalizing |
| 286 | - the search slightly when the only difference between the |
| 287 | - characters is in its case. For instance, 'c' vs. 'C'. |
| 288 | - </_description> |
| 289 | - </key> |
| 290 | - <key name="max-distance" type="u"> |
| 291 | - <default>30</default> |
| 292 | - <_summary>The highest distance value that is shown in the results</_summary> |
| 293 | - <_description> |
| 294 | - After the distances are calculated (including the indicator |
| 295 | - penalty) then all values above this max are dropped. This |
| 296 | - means that the history for those entries aren't looked up |
| 297 | - as well. |
| 298 | - </_description> |
| 299 | - </key> |
| 300 | - </schema> |
| 301 | -</schemalist> |
| 302 | |
| 303 | === modified file 'po/POTFILES.in' |
| 304 | --- po/POTFILES.in 2012-03-16 14:48:19 +0000 |
| 305 | +++ po/POTFILES.in 2012-04-03 12:30:26 +0000 |
| 306 | @@ -1,7 +1,6 @@ |
| 307 | -data/com.canonical.indicator.appmenu.gschema.xml.in |
| 308 | -data/com.canonical.indicator.appmenu.hud.gschema.xml.in |
| 309 | -data/com.canonical.indicator.appmenu.hud.search.gschema.xml.in |
| 310 | -src/distance.c |
| 311 | +data/com.canonical.indicator.appmenu.gschema.xml |
| 312 | +data/com.canonical.indicator.appmenu.hud.gschema.xml |
| 313 | +data/com.canonical.indicator.appmenu.hud.search.gschema.xml |
| 314 | src/dump-app-info.c |
| 315 | src/gdk-get-func.c |
| 316 | src/hudappindicatorsource.c |
| 317 | @@ -18,6 +17,7 @@ |
| 318 | src/hudsource.c |
| 319 | src/hudsourcelist.c |
| 320 | src/hudstringlist.c |
| 321 | +src/hudtoken.c |
| 322 | src/hud-verify-app-info.c |
| 323 | src/hudwindowsource.c |
| 324 | src/indicator-appmenu.c |
| 325 | |
| 326 | === modified file 'src/Makefile.am' |
| 327 | --- src/Makefile.am 2012-03-21 17:27:26 +0000 |
| 328 | +++ src/Makefile.am 2012-04-03 12:30:26 +0000 |
| 329 | @@ -99,8 +99,6 @@ |
| 330 | libhud_service_a_SOURCES = \ |
| 331 | create-db.h \ |
| 332 | create-db.c \ |
| 333 | - distance.h \ |
| 334 | - distance.c \ |
| 335 | dump-app-info.h \ |
| 336 | dump-app-info.c \ |
| 337 | shared-values.h \ |
| 338 | @@ -124,6 +122,8 @@ |
| 339 | hudsourcelist.c \ |
| 340 | hudsource.h \ |
| 341 | hudsource.c \ |
| 342 | + hudtoken.h \ |
| 343 | + hudtoken.c \ |
| 344 | hudquery.h \ |
| 345 | hudquery.c \ |
| 346 | hudstringlist.h \ |
| 347 | |
| 348 | === removed file 'src/distance.c' |
| 349 | --- src/distance.c 2012-03-29 15:26:51 +0000 |
| 350 | +++ src/distance.c 1970-01-01 00:00:00 +0000 |
| 351 | @@ -1,415 +0,0 @@ |
| 352 | -/* |
| 353 | -Functions to calculate the distance between two strings. |
| 354 | - |
| 355 | -Copyright 2011 Canonical Ltd. |
| 356 | - |
| 357 | -Authors: |
| 358 | - Ted Gould <ted@canonical.com> |
| 359 | - |
| 360 | -This program is free software: you can redistribute it and/or modify it |
| 361 | -under the terms of the GNU General Public License version 3, as published |
| 362 | -by the Free Software Foundation. |
| 363 | - |
| 364 | -This program is distributed in the hope that it will be useful, but |
| 365 | -WITHOUT ANY WARRANTY; without even the implied warranties of |
| 366 | -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
| 367 | -PURPOSE. See the GNU General Public License for more details. |
| 368 | - |
| 369 | -You should have received a copy of the GNU General Public License along |
| 370 | -with this program. If not, see <http://www.gnu.org/licenses/>. |
| 371 | -*/ |
| 372 | - |
| 373 | -#include "distance.h" |
| 374 | - |
| 375 | -#include <glib-object.h> |
| 376 | -#include <glib/gprintf.h> |
| 377 | -#include <glib/gi18n.h> |
| 378 | -#include <gio/gio.h> |
| 379 | - |
| 380 | -#include "hudsettings.h" |
| 381 | - |
| 382 | -#define ADD_PENALTY hud_settings.add_penalty |
| 383 | -#define PRE_ADD_PENALTY hud_settings.add_penalty_pre |
| 384 | -#define DROP_PENALTY hud_settings.drop_penalty |
| 385 | -#define END_DROP_PENALTY hud_settings.drop_penalty_end |
| 386 | -#define TRANSPOSE_PENALTY hud_settings.transpose_penalty |
| 387 | -#define SWAP_PENALTY hud_settings.swap_penalty |
| 388 | -#define SWAP_CASE_PENALTY hud_settings.swap_penalty_case |
| 389 | - |
| 390 | -/* Figure out how far off we are from a set of characters, basically |
| 391 | - whether they match or not */ |
| 392 | -static guint |
| 393 | -swap_cost (gunichar a, gunichar b) |
| 394 | -{ |
| 395 | - if (a == b) |
| 396 | - return 0; |
| 397 | - if (g_unichar_toupper(a) == g_unichar_toupper(b)) |
| 398 | - return SWAP_CASE_PENALTY; /* Some penalty, but close */ |
| 399 | - return SWAP_PENALTY; |
| 400 | -} |
| 401 | - |
| 402 | -#define MATRIX_VAL(needle_loc, haystack_loc) (penalty_matrix[(needle_loc + 1) + (haystack_loc + 1) * (len_needle + 1)]) |
| 403 | - |
| 404 | -/* A function to print out the matrix for debugging purposes. It is |
| 405 | - mostly used in the test suite, though it could be used else where if |
| 406 | - needed */ |
| 407 | -static void |
| 408 | -dumpmatrix (const gchar * needle, guint len_needle, const gchar * haystack, guint len_haystack, guint * penalty_matrix) |
| 409 | -{ |
| 410 | -#ifndef DUMP_MATRIX |
| 411 | - return; |
| 412 | -#endif |
| 413 | - gint i, j; |
| 414 | - |
| 415 | - g_printf("\n"); |
| 416 | - /* Character Column */ |
| 417 | - g_printf(" "); |
| 418 | - /* Base val column */ |
| 419 | - g_printf(" "); |
| 420 | - for (i = 0; i < len_needle; i++) { |
| 421 | - g_printf("%c ", needle[i]); |
| 422 | - } |
| 423 | - g_printf("\n"); |
| 424 | - |
| 425 | - /* Character Column */ |
| 426 | - g_printf(" "); |
| 427 | - for (i = -1; i < (gint)len_needle; i++) { |
| 428 | - g_printf("%u ", MATRIX_VAL(i, -1)); |
| 429 | - } |
| 430 | - g_printf("\n"); |
| 431 | - |
| 432 | - for (j = 0; j < len_haystack; j++) { |
| 433 | - g_printf("%c ", haystack[j]); |
| 434 | - for (i = -1; i < (gint)len_needle; i++) { |
| 435 | - g_printf("%u ", MATRIX_VAL(i, j)); |
| 436 | - } |
| 437 | - g_printf("\n"); |
| 438 | - } |
| 439 | - g_printf("\n"); |
| 440 | - |
| 441 | - return; |
| 442 | -} |
| 443 | - |
| 444 | -/* Looks at two individual tokens, builds a matrix and calculates the |
| 445 | - distance between them by moving through the matrix. |
| 446 | - If you believe, you can see the matrix, otherwise it looks just like |
| 447 | - an array. */ |
| 448 | -static guint |
| 449 | -calculate_token_distance (const gchar * needle, const gchar * haystack) |
| 450 | -{ |
| 451 | - // g_debug("Comparing token '%s' to token '%s'", needle, haystack); |
| 452 | - |
| 453 | - guint len_needle = 0; |
| 454 | - guint len_haystack = 0; |
| 455 | - |
| 456 | - if (needle != NULL) { |
| 457 | - len_needle = g_utf8_strlen(needle, -1); |
| 458 | - } |
| 459 | - |
| 460 | - if (haystack != NULL) { |
| 461 | - len_haystack = g_utf8_strlen(haystack, -1); |
| 462 | - } |
| 463 | - |
| 464 | - /* Handle the cases of very short or NULL strings quickly */ |
| 465 | - if (len_needle == 0) { |
| 466 | - return DROP_PENALTY * len_haystack; |
| 467 | - } |
| 468 | - |
| 469 | - if (len_haystack == 0) { |
| 470 | - return ADD_PENALTY * len_needle; |
| 471 | - } |
| 472 | - |
| 473 | - /* Allocate the matrix of penalties */ |
| 474 | - guint * penalty_matrix = g_malloc0(sizeof(guint) * (len_needle + 1) * (len_haystack + 1)); |
| 475 | - gint len_difference; |
| 476 | - gint penalty; |
| 477 | - int i; |
| 478 | - |
| 479 | - /* Take the first row and first column and make them additional letter penalties */ |
| 480 | - penalty = 0; |
| 481 | - for (i = 0; i < len_needle + 1; i++) { |
| 482 | - MATRIX_VAL(i - 1, -1) = penalty; |
| 483 | - penalty += ADD_PENALTY; |
| 484 | - } |
| 485 | - |
| 486 | - len_difference = len_haystack - len_needle; |
| 487 | - penalty = 0; |
| 488 | - for (i = 0; i < len_difference; i++) { |
| 489 | - MATRIX_VAL(-1, i - 1) = penalty; |
| 490 | - penalty += PRE_ADD_PENALTY; |
| 491 | - } |
| 492 | - for (; i < len_haystack + 1; i++) { |
| 493 | - MATRIX_VAL(-1, i - 1) = penalty; |
| 494 | - penalty += DROP_PENALTY; |
| 495 | - } |
| 496 | - |
| 497 | - /* Now go through the matrix building up the penalties */ |
| 498 | - int ineedle, ihaystack; |
| 499 | - gchar * needle_head, * haystack_head; |
| 500 | - for (needle_head = (gchar *)needle, ineedle = 0; |
| 501 | - needle_head[0] != '\0'; |
| 502 | - needle_head = g_utf8_next_char(needle_head), ineedle++) { |
| 503 | - for (haystack_head = (gchar *)haystack, ihaystack = 0; |
| 504 | - haystack_head[0] != '\0'; |
| 505 | - haystack_head = g_utf8_next_char(haystack_head), ihaystack++) { |
| 506 | - gunichar needle_let = g_utf8_get_char(needle_head); |
| 507 | - gunichar haystack_let = g_utf8_get_char(haystack_head); |
| 508 | - |
| 509 | - guint subst_pen = MATRIX_VAL(ineedle - 1, ihaystack - 1) + swap_cost(needle_let, haystack_let); |
| 510 | - guint drop_pen = MATRIX_VAL(ineedle - 1, ihaystack); |
| 511 | - if (ineedle < ihaystack) { |
| 512 | - drop_pen += DROP_PENALTY; |
| 513 | - } else { |
| 514 | - drop_pen += END_DROP_PENALTY; |
| 515 | - } |
| 516 | - |
| 517 | - guint add_pen = MATRIX_VAL(ineedle, ihaystack - 1); |
| 518 | - if (len_haystack - len_needle - ineedle > 0) { |
| 519 | - add_pen += PRE_ADD_PENALTY; |
| 520 | - } else { |
| 521 | - add_pen += ADD_PENALTY; |
| 522 | - } |
| 523 | - guint transpose_pen = drop_pen + 1; /* ensures won't be chosen */ |
| 524 | - |
| 525 | - if (ineedle > 0 && ihaystack > 0 && needle_let == haystack[ihaystack - 1] && haystack_let == needle[ineedle - 1]) { |
| 526 | - transpose_pen = MATRIX_VAL(ineedle - 2, ihaystack - 2) + TRANSPOSE_PENALTY; |
| 527 | - } |
| 528 | - |
| 529 | - MATRIX_VAL(ineedle, ihaystack) = MIN(MIN(subst_pen, drop_pen), MIN(add_pen, transpose_pen)); |
| 530 | - } |
| 531 | - } |
| 532 | - |
| 533 | - dumpmatrix(needle, len_needle, haystack, len_haystack, penalty_matrix); |
| 534 | - |
| 535 | - guint retval = penalty_matrix[(len_needle + 1) * (len_haystack + 1) - 1]; |
| 536 | - g_free(penalty_matrix); |
| 537 | - |
| 538 | - return retval; |
| 539 | -} |
| 540 | - |
| 541 | -/* Looks through the array of paths and tries to find a minimum path |
| 542 | - looking up from the needle specified. This way we can look through |
| 543 | - all the possible paths */ |
| 544 | -guint |
| 545 | -minimize_distance_recurse (guint needle, guint num_needles, guint haystack, guint num_haystacks, guint * distances, guint * matches) |
| 546 | -{ |
| 547 | - gint i; |
| 548 | - |
| 549 | - /* Put where we are in the array so that we don't forget */ |
| 550 | - matches[needle] = haystack; |
| 551 | - |
| 552 | - /* First check to see if we've already used this entry */ |
| 553 | - for (i = needle - 1; i >= 0; i--) { |
| 554 | - if (matches[i] == haystack) { |
| 555 | - return G_MAXUINT; |
| 556 | - } |
| 557 | - } |
| 558 | - |
| 559 | - /* If we're the last needle, we can return our distance */ |
| 560 | - if (needle + 1 >= num_needles) { |
| 561 | - return distances[(needle * num_haystacks) + haystack]; |
| 562 | - } |
| 563 | - |
| 564 | - guint * local_match = g_new0(guint, num_needles); |
| 565 | - for (i = 0; i < num_needles && i < needle + 1; i++) { |
| 566 | - local_match[i] = matches[i]; |
| 567 | - } |
| 568 | - |
| 569 | - /* Now look where we can get the minimum with the other needles */ |
| 570 | - guint min = G_MAXUINT; |
| 571 | - for (i = 0; i < num_haystacks; i++) { |
| 572 | - guint local = minimize_distance_recurse(needle + 1, num_needles, i, num_haystacks, distances, local_match); |
| 573 | - |
| 574 | - if (local < min) { |
| 575 | - min = local; |
| 576 | - |
| 577 | - int j; |
| 578 | - for (j = needle + 1; j < num_needles; j++) { |
| 579 | - matches[j] = local_match[j]; |
| 580 | - } |
| 581 | - } |
| 582 | - } |
| 583 | - |
| 584 | - g_free(local_match); |
| 585 | - |
| 586 | - /* Return the min of everyone else plus our distance */ |
| 587 | - if (min < G_MAXUINT) { |
| 588 | - min += distances[(needle * num_haystacks) + haystack]; |
| 589 | - } |
| 590 | - |
| 591 | - return min; |
| 592 | -} |
| 593 | - |
| 594 | -/* Figuring out the lowest path through the distance array |
| 595 | - where we don't use the same haystack tokens */ |
| 596 | -guint |
| 597 | -minimize_distance (guint num_needles, guint num_haystacks, guint * distances, guint * matches) |
| 598 | -{ |
| 599 | - guint final_distance = G_MAXUINT; |
| 600 | - guint * local_matches = g_new0(guint, num_needles); |
| 601 | - |
| 602 | - guint haystack_token; |
| 603 | - for (haystack_token = 0; haystack_token < num_haystacks; haystack_token++) { |
| 604 | - guint distance = minimize_distance_recurse(0, num_needles, haystack_token, num_haystacks, distances, local_matches); |
| 605 | - |
| 606 | - if (distance < final_distance) { |
| 607 | - final_distance = distance; |
| 608 | - |
| 609 | - guint match_cnt; |
| 610 | - for (match_cnt = 0; match_cnt < num_needles; match_cnt++) { |
| 611 | - matches[match_cnt] = local_matches[match_cnt]; |
| 612 | - } |
| 613 | - } |
| 614 | - } |
| 615 | - |
| 616 | - g_free(local_matches); |
| 617 | - |
| 618 | - return final_distance; |
| 619 | -} |
| 620 | - |
| 621 | -/* Dups a specific token in the array of strv arrays */ |
| 622 | -gchar * |
| 623 | -find_token (guint token_number, GStrv * haystacks, guint num_haystacks) |
| 624 | -{ |
| 625 | - guint haystack; |
| 626 | - |
| 627 | - for (haystack = 0; haystack < num_haystacks; haystack++) { |
| 628 | - guint strvlen = g_strv_length(haystacks[haystack]); |
| 629 | - |
| 630 | - if (token_number < strvlen) { |
| 631 | - return g_strdup(haystacks[haystack][token_number]); |
| 632 | - } |
| 633 | - |
| 634 | - token_number -= strvlen; |
| 635 | - } |
| 636 | - |
| 637 | - return NULL; |
| 638 | -} |
| 639 | - |
| 640 | -/* Normalize a set of strings so that we can compare them |
| 641 | - without worry about accents and the such */ |
| 642 | -static GStrv |
| 643 | -normalize_gstrv (GStrv inlist) |
| 644 | -{ |
| 645 | - /* The identity property */ |
| 646 | - if (inlist == NULL) return NULL; |
| 647 | - |
| 648 | - guint length = g_strv_length(inlist); |
| 649 | - GStrv out = g_new0(gchar *, length + 1); |
| 650 | - gint i; |
| 651 | - |
| 652 | - for (i = 0; i < length; i++) { |
| 653 | - out[i] = g_utf8_normalize(inlist[i], -1, G_NORMALIZE_ALL); |
| 654 | - } |
| 655 | - |
| 656 | - return out; |
| 657 | -} |
| 658 | - |
| 659 | -#define SEPARATORS " .->" |
| 660 | - |
| 661 | -guint |
| 662 | -calculate_distance (const gchar * needle, GStrv haystacks, GStrv * matches) |
| 663 | -{ |
| 664 | - g_return_val_if_fail(needle != NULL || haystacks != NULL, G_MAXUINT); |
| 665 | - guint final_distance = G_MAXUINT; |
| 666 | - |
| 667 | - if (needle == NULL || !g_utf8_validate(needle, -1, NULL)) { |
| 668 | - return DROP_PENALTY * g_utf8_strlen(haystacks[0], 1024); |
| 669 | - } |
| 670 | - if (haystacks == NULL) { |
| 671 | - return ADD_PENALTY * g_utf8_strlen(needle, 1024); |
| 672 | - } |
| 673 | - |
| 674 | - /* Check to make sure we have valid haystacks */ |
| 675 | - gint i; |
| 676 | - guint num_haystacks = g_strv_length(haystacks); |
| 677 | - |
| 678 | - for (i = 0; i < num_haystacks; i++) { |
| 679 | - if (!g_utf8_validate(haystacks[i], -1, NULL)) { |
| 680 | - return ADD_PENALTY * g_utf8_strlen(needle, 1024); |
| 681 | - } |
| 682 | - } |
| 683 | - |
| 684 | - /* Tokenize all the haystack strings */ |
| 685 | - guint num_haystack_tokens = 0; |
| 686 | - GStrv * haystacks_array = g_new0(GStrv, num_haystacks); |
| 687 | - |
| 688 | - for (i = 0; i < num_haystacks; i++) { |
| 689 | - GStrv split = g_strsplit_set(haystacks[i], SEPARATORS, 0); |
| 690 | - haystacks_array[i] = normalize_gstrv(split); |
| 691 | - g_strfreev(split); |
| 692 | - num_haystack_tokens += g_strv_length(haystacks_array[i]); |
| 693 | - } |
| 694 | - |
| 695 | - /* Tokenize our needles the same way */ |
| 696 | - GStrv needle_split = g_strsplit_set(needle, SEPARATORS, 0); |
| 697 | - GStrv needle_tokens = normalize_gstrv(needle_split); |
| 698 | - g_strfreev(needle_split); |
| 699 | - guint num_needle_tokens = g_strv_length(needle_tokens); |
| 700 | - |
| 701 | - /* If we can't even find a set that works, let's just cut |
| 702 | - our losses here */ |
| 703 | - if (num_needle_tokens > num_haystack_tokens) { |
| 704 | - goto cleanup_tokens; |
| 705 | - } |
| 706 | - |
| 707 | - /* We need a place to store all the distances */ |
| 708 | - guint * distances = g_new0(guint, num_haystack_tokens * num_needle_tokens); |
| 709 | - |
| 710 | - /* Calculate all the distance combinations */ |
| 711 | - gint needle_token; |
| 712 | - for (needle_token = 0; needle_tokens[needle_token] != NULL; needle_token++) { |
| 713 | - gchar * ineedle = needle_tokens[needle_token]; |
| 714 | - |
| 715 | - guint haystacks_cnt; |
| 716 | - guint haystack_token_cnt = 0; |
| 717 | - for (haystacks_cnt = 0; haystacks_cnt < num_haystacks; haystacks_cnt++) { |
| 718 | - guint haystack_cnt; |
| 719 | - for (haystack_cnt = 0; haystacks_array[haystacks_cnt][haystack_cnt] != NULL; haystack_cnt++) { |
| 720 | - gchar * ihaystack = haystacks_array[haystacks_cnt][haystack_cnt]; |
| 721 | - guint distance = calculate_token_distance(ineedle, ihaystack); |
| 722 | - |
| 723 | - distances[(needle_token * num_haystack_tokens) + haystack_token_cnt] = distance; |
| 724 | - haystack_token_cnt++; |
| 725 | - } |
| 726 | - } |
| 727 | - } |
| 728 | - |
| 729 | - /* Now, try to find a path through the array that results in the |
| 730 | - lowest total value */ |
| 731 | - guint * final_matches = g_new0(guint, num_needle_tokens); |
| 732 | - |
| 733 | - final_distance = minimize_distance(num_needle_tokens, num_haystack_tokens, distances, final_matches); |
| 734 | - |
| 735 | - /* Set up an array for matches so that we can enter |
| 736 | - the items as we go */ |
| 737 | - if (matches != NULL) { |
| 738 | - GStrv match_array = NULL; |
| 739 | - match_array = g_new0(gchar *, num_needle_tokens + 1); |
| 740 | - match_array[num_needle_tokens] = NULL; |
| 741 | - |
| 742 | - /* Copy the strings that we care about */ |
| 743 | - int i; |
| 744 | - for (i = 0; i < num_needle_tokens; i++) { |
| 745 | - match_array[i] = find_token(final_matches[i], haystacks_array, num_haystacks); |
| 746 | - } |
| 747 | - |
| 748 | - *matches = match_array; |
| 749 | - } |
| 750 | - |
| 751 | - g_free(final_matches); |
| 752 | - g_free(distances); |
| 753 | - |
| 754 | -cleanup_tokens: |
| 755 | - g_strfreev(needle_tokens); |
| 756 | - for (i = 0; i < num_haystacks; i++) { |
| 757 | - g_strfreev(haystacks_array[i]); |
| 758 | - } |
| 759 | - g_free(haystacks_array); |
| 760 | - |
| 761 | - if (final_distance != G_MAXUINT) { |
| 762 | - return final_distance / num_needle_tokens; |
| 763 | - } else { |
| 764 | - return G_MAXUINT; |
| 765 | - } |
| 766 | -} |
| 767 | |
| 768 | === removed file 'src/distance.h' |
| 769 | --- src/distance.h 2012-03-05 04:46:53 +0000 |
| 770 | +++ src/distance.h 1970-01-01 00:00:00 +0000 |
| 771 | @@ -1,33 +0,0 @@ |
| 772 | -/* |
| 773 | -Functions to calculate the distance between two strings. |
| 774 | - |
| 775 | -Copyright 2011 Canonical Ltd. |
| 776 | - |
| 777 | -Authors: |
| 778 | - Ted Gould <ted@canonical.com> |
| 779 | - |
| 780 | -This program is free software: you can redistribute it and/or modify it |
| 781 | -under the terms of the GNU General Public License version 3, as published |
| 782 | -by the Free Software Foundation. |
| 783 | - |
| 784 | -This program is distributed in the hope that it will be useful, but |
| 785 | -WITHOUT ANY WARRANTY; without even the implied warranties of |
| 786 | -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
| 787 | -PURPOSE. See the GNU General Public License for more details. |
| 788 | - |
| 789 | -You should have received a copy of the GNU General Public License along |
| 790 | -with this program. If not, see <http://www.gnu.org/licenses/>. |
| 791 | -*/ |
| 792 | - |
| 793 | -#ifndef __DISTANCE_H__ |
| 794 | -#define __DISTANCE_H__ |
| 795 | - |
| 796 | -#include <glib-object.h> |
| 797 | - |
| 798 | -G_BEGIN_DECLS |
| 799 | - |
| 800 | -guint calculate_distance (const gchar * needle, GStrv haystack, GStrv * matched); |
| 801 | - |
| 802 | -G_END_DECLS |
| 803 | - |
| 804 | -#endif /* __DISTANCE_H__ */ |
| 805 | |
| 806 | === modified file 'src/hudappindicatorsource.c' |
| 807 | --- src/hudappindicatorsource.c 2012-03-23 15:35:01 +0000 |
| 808 | +++ src/hudappindicatorsource.c 2012-04-03 12:30:26 +0000 |
| 809 | @@ -371,9 +371,9 @@ |
| 810 | } |
| 811 | |
| 812 | static void |
| 813 | -hud_app_indicator_source_search (HudSource *hud_source, |
| 814 | - GPtrArray *results_array, |
| 815 | - const gchar *search_string) |
| 816 | +hud_app_indicator_source_search (HudSource *hud_source, |
| 817 | + GPtrArray *results_array, |
| 818 | + HudTokenList *search_tokens) |
| 819 | { |
| 820 | HudAppIndicatorSource *source = HUD_APP_INDICATOR_SOURCE (hud_source); |
| 821 | GSequenceIter *iter; |
| 822 | @@ -382,7 +382,7 @@ |
| 823 | |
| 824 | while (!g_sequence_iter_is_end (iter)) |
| 825 | { |
| 826 | - hud_source_search (g_sequence_get (iter), results_array, search_string); |
| 827 | + hud_source_search (g_sequence_get (iter), results_array, search_tokens); |
| 828 | iter = g_sequence_iter_next (iter); |
| 829 | } |
| 830 | } |
| 831 | |
| 832 | === modified file 'src/huddbusmenucollector.c' |
| 833 | --- src/huddbusmenucollector.c 2012-03-26 15:25:33 +0000 |
| 834 | +++ src/huddbusmenucollector.c 2012-04-03 12:30:26 +0000 |
| 835 | @@ -290,9 +290,9 @@ |
| 836 | } |
| 837 | |
| 838 | static void |
| 839 | -hud_dbusmenu_collector_search (HudSource *source, |
| 840 | - GPtrArray *results_array, |
| 841 | - const gchar *search_string) |
| 842 | +hud_dbusmenu_collector_search (HudSource *source, |
| 843 | + GPtrArray *results_array, |
| 844 | + HudTokenList *search_string) |
| 845 | { |
| 846 | HudDbusmenuCollector *collector = HUD_DBUSMENU_COLLECTOR (source); |
| 847 | GHashTableIter iter; |
| 848 | |
| 849 | === modified file 'src/huddebugsource.c' |
| 850 | --- src/huddebugsource.c 2012-03-23 15:35:01 +0000 |
| 851 | +++ src/huddebugsource.c 2012-04-03 12:30:26 +0000 |
| 852 | @@ -105,9 +105,9 @@ |
| 853 | } |
| 854 | |
| 855 | static void |
| 856 | -hud_debug_source_search (HudSource *hud_source, |
| 857 | - GPtrArray *results_array, |
| 858 | - const gchar *search_string) |
| 859 | +hud_debug_source_search (HudSource *hud_source, |
| 860 | + GPtrArray *results_array, |
| 861 | + HudTokenList *search_string) |
| 862 | { |
| 863 | HudDebugSource *source = HUD_DEBUG_SOURCE (hud_source); |
| 864 | |
| 865 | |
| 866 | === modified file 'src/hudindicatorsource.c' |
| 867 | --- src/hudindicatorsource.c 2012-03-23 15:35:01 +0000 |
| 868 | +++ src/hudindicatorsource.c 2012-04-03 12:30:26 +0000 |
| 869 | @@ -146,9 +146,9 @@ |
| 870 | } |
| 871 | |
| 872 | static void |
| 873 | -hud_indicator_source_search (HudSource *hud_source, |
| 874 | - GPtrArray *results_array, |
| 875 | - const gchar *search_string) |
| 876 | +hud_indicator_source_search (HudSource *hud_source, |
| 877 | + GPtrArray *results_array, |
| 878 | + HudTokenList *search_string) |
| 879 | { |
| 880 | HudIndicatorSource *source = HUD_INDICATOR_SOURCE (hud_source); |
| 881 | gint i; |
| 882 | |
| 883 | === modified file 'src/huditem.c' |
| 884 | --- src/huditem.c 2012-03-23 15:35:01 +0000 |
| 885 | +++ src/huditem.c 2012-04-03 12:30:26 +0000 |
| 886 | @@ -19,6 +19,7 @@ |
| 887 | #include "huditem.h" |
| 888 | |
| 889 | #include "usage-tracker.h" |
| 890 | +#include "hudtoken.h" |
| 891 | |
| 892 | /** |
| 893 | * SECTION:huditem |
| 894 | @@ -52,6 +53,7 @@ |
| 895 | |
| 896 | gchar *desktop_file; |
| 897 | |
| 898 | + HudTokenList *token_list; |
| 899 | HudStringList *tokens; |
| 900 | gchar *usage_tag; |
| 901 | gchar *app_icon; |
| 902 | @@ -68,8 +70,10 @@ |
| 903 | HudItem *item = HUD_ITEM (object); |
| 904 | |
| 905 | g_hash_table_remove (hud_item_table, &item->priv->id); |
| 906 | + hud_token_list_free (item->priv->token_list); |
| 907 | hud_string_list_unref (item->priv->tokens); |
| 908 | g_free (item->priv->desktop_file); |
| 909 | + g_free (item->priv->app_icon); |
| 910 | g_free (item->priv->usage_tag); |
| 911 | |
| 912 | G_OBJECT_CLASS (hud_item_parent_class) |
| 913 | @@ -156,6 +160,7 @@ |
| 914 | item->priv->app_icon = g_strdup (app_icon); |
| 915 | item->priv->enabled = enabled; |
| 916 | item->priv->id = hud_item_next_id++; |
| 917 | + item->priv->token_list = hud_token_list_new_from_string_list (tokens); |
| 918 | |
| 919 | g_hash_table_insert (hud_item_table, &item->priv->id, item); |
| 920 | |
| 921 | @@ -330,3 +335,9 @@ |
| 922 | { |
| 923 | return g_hash_table_lookup (hud_item_table, &id); |
| 924 | } |
| 925 | + |
| 926 | +HudTokenList * |
| 927 | +hud_item_get_token_list (HudItem *item) |
| 928 | +{ |
| 929 | + return item->priv->token_list; |
| 930 | +} |
| 931 | |
| 932 | === modified file 'src/huditem.h' |
| 933 | --- src/huditem.h 2012-03-23 15:35:01 +0000 |
| 934 | +++ src/huditem.h 2012-04-03 12:30:26 +0000 |
| 935 | @@ -21,7 +21,7 @@ |
| 936 | |
| 937 | #include <glib-object.h> |
| 938 | |
| 939 | -#include "hudstringlist.h" |
| 940 | +#include "hudtoken.h" |
| 941 | |
| 942 | #define HUD_TYPE_ITEM (hud_item_get_type ()) |
| 943 | #define HUD_ITEM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ |
| 944 | @@ -74,5 +74,6 @@ |
| 945 | gboolean hud_item_get_enabled (HudItem *item); |
| 946 | guint64 hud_item_get_id (HudItem *item); |
| 947 | HudItem * hud_item_lookup (guint64 id); |
| 948 | +HudTokenList * hud_item_get_token_list (HudItem *item); |
| 949 | |
| 950 | #endif /* __HUD_ITEM_H__ */ |
| 951 | |
| 952 | === modified file 'src/hudmenumodelcollector.c' |
| 953 | --- src/hudmenumodelcollector.c 2012-03-23 15:35:42 +0000 |
| 954 | +++ src/hudmenumodelcollector.c 2012-04-03 12:30:26 +0000 |
| 955 | @@ -346,9 +346,9 @@ |
| 956 | } |
| 957 | |
| 958 | static void |
| 959 | -hud_menu_model_collector_search (HudSource *source, |
| 960 | - GPtrArray *results_array, |
| 961 | - const gchar *search_string) |
| 962 | +hud_menu_model_collector_search (HudSource *source, |
| 963 | + GPtrArray *results_array, |
| 964 | + HudTokenList *search_string) |
| 965 | { |
| 966 | HudMenuModelCollector *collector = HUD_MENU_MODEL_COLLECTOR (source); |
| 967 | GPtrArray *items; |
| 968 | |
| 969 | === modified file 'src/hudquery.c' |
| 970 | --- src/hudquery.c 2012-03-23 15:35:42 +0000 |
| 971 | +++ src/hudquery.c 2012-04-03 12:30:26 +0000 |
| 972 | @@ -51,6 +51,7 @@ |
| 973 | |
| 974 | HudSource *source; |
| 975 | gchar *search_string; |
| 976 | + HudTokenList *token_list; |
| 977 | gint num_results; |
| 978 | guint refresh_id; |
| 979 | |
| 980 | @@ -100,16 +101,21 @@ |
| 981 | hud_query_refresh (HudQuery *query) |
| 982 | { |
| 983 | guint max_usage = 0; |
| 984 | + guint64 start_time; |
| 985 | + |
| 986 | + start_time = g_get_monotonic_time (); |
| 987 | |
| 988 | g_ptr_array_set_size (query->results, 0); |
| 989 | |
| 990 | if (query->search_string[0] != '\0') |
| 991 | - hud_source_search (query->source, query->results, query->search_string); |
| 992 | + hud_source_search (query->source, query->results, query->token_list); |
| 993 | |
| 994 | g_ptr_array_foreach (query->results, hud_query_find_max_usage, &max_usage); |
| 995 | g_ptr_array_sort_with_data (query->results, hud_query_compare_results, GINT_TO_POINTER (max_usage)); |
| 996 | if (query->results->len > query->num_results) |
| 997 | g_ptr_array_set_size (query->results, query->num_results); |
| 998 | + |
| 999 | + g_debug ("query took %dus\n", (int) (g_get_monotonic_time () - start_time)); |
| 1000 | } |
| 1001 | |
| 1002 | static gboolean |
| 1003 | @@ -148,6 +154,7 @@ |
| 1004 | hud_source_unuse (query->source); |
| 1005 | |
| 1006 | g_object_unref (query->source); |
| 1007 | + hud_token_list_free (query->token_list); |
| 1008 | g_free (query->search_string); |
| 1009 | g_ptr_array_unref (query->results); |
| 1010 | |
| 1011 | @@ -206,6 +213,7 @@ |
| 1012 | query->source = g_object_ref (source); |
| 1013 | query->results = g_ptr_array_new_with_free_func (g_object_unref); |
| 1014 | query->search_string = g_strdup (search_string); |
| 1015 | + query->token_list = hud_token_list_new_from_string (query->search_string); |
| 1016 | query->num_results = num_results; |
| 1017 | |
| 1018 | hud_source_use (query->source); |
| 1019 | |
| 1020 | === modified file 'src/hudresult.c' |
| 1021 | --- src/hudresult.c 2012-03-12 16:27:09 +0000 |
| 1022 | +++ src/hudresult.c 2012-04-03 12:30:26 +0000 |
| 1023 | @@ -21,7 +21,7 @@ |
| 1024 | #include <string.h> |
| 1025 | |
| 1026 | #include "hudsettings.h" |
| 1027 | -#include "distance.h" |
| 1028 | +#include "hudtoken.h" |
| 1029 | |
| 1030 | /** |
| 1031 | * SECTION:hudresult |
| 1032 | @@ -48,46 +48,19 @@ |
| 1033 | HudItem *item; |
| 1034 | |
| 1035 | guint distance; |
| 1036 | - gchar **matched; |
| 1037 | + const gchar **matched; |
| 1038 | gchar *description; |
| 1039 | }; |
| 1040 | |
| 1041 | G_DEFINE_TYPE (HudResult, hud_result, G_TYPE_OBJECT) |
| 1042 | |
| 1043 | -static guint |
| 1044 | -calculate_distance_from_list (const gchar *search_string, |
| 1045 | - HudStringList *list, |
| 1046 | - GStrv *matched) |
| 1047 | -{ |
| 1048 | - HudStringList *iter; |
| 1049 | - const gchar **strv; |
| 1050 | - guint distance; |
| 1051 | - gint i = 0; |
| 1052 | - |
| 1053 | - for (iter = list; iter; iter = hud_string_list_get_tail (iter)) |
| 1054 | - i++; |
| 1055 | - |
| 1056 | - strv = g_new (const char *, i + 1); |
| 1057 | - strv[i] = NULL; |
| 1058 | - |
| 1059 | - for (iter = list; iter; iter = hud_string_list_get_tail (iter)) |
| 1060 | - strv[--i] = hud_string_list_get_head (iter); |
| 1061 | - |
| 1062 | - distance = calculate_distance (search_string, (char **) strv, matched); |
| 1063 | - |
| 1064 | - g_free (strv); |
| 1065 | - |
| 1066 | - |
| 1067 | - return distance; |
| 1068 | -} |
| 1069 | - |
| 1070 | static void |
| 1071 | hud_result_finalize (GObject *object) |
| 1072 | { |
| 1073 | HudResult *result = HUD_RESULT (object); |
| 1074 | |
| 1075 | g_object_unref (result->item); |
| 1076 | - g_strfreev (result->matched); |
| 1077 | + g_free (result->matched); |
| 1078 | g_free (result->description); |
| 1079 | |
| 1080 | G_OBJECT_CLASS (hud_result_parent_class) |
| 1081 | @@ -126,16 +99,16 @@ |
| 1082 | * Returns: a new #HudResult, or %NULL in event of a poor match |
| 1083 | **/ |
| 1084 | HudResult * |
| 1085 | -hud_result_get_if_matched (HudItem *item, |
| 1086 | - const gchar *search_string, |
| 1087 | - guint penalty) |
| 1088 | +hud_result_get_if_matched (HudItem *item, |
| 1089 | + HudTokenList *search_tokens, |
| 1090 | + guint penalty) |
| 1091 | { |
| 1092 | if (!hud_item_get_enabled (item)) |
| 1093 | return NULL; |
| 1094 | |
| 1095 | /* ignore the penalty in the max-distance calculation */ |
| 1096 | - if (calculate_distance_from_list (search_string, hud_item_get_tokens (item), NULL) <= hud_settings.max_distance) |
| 1097 | - return hud_result_new (item, search_string, penalty); |
| 1098 | + if (hud_token_list_distance (hud_item_get_token_list (item), search_tokens, NULL) <= hud_settings.max_distance) |
| 1099 | + return hud_result_new (item, search_tokens, penalty); |
| 1100 | else |
| 1101 | return NULL; |
| 1102 | } |
| 1103 | @@ -212,18 +185,18 @@ |
| 1104 | * Returns: the new #HudResult |
| 1105 | **/ |
| 1106 | HudResult * |
| 1107 | -hud_result_new (HudItem *item, |
| 1108 | - const gchar *search_string, |
| 1109 | - guint penalty) |
| 1110 | +hud_result_new (HudItem *item, |
| 1111 | + HudTokenList *search_tokens, |
| 1112 | + guint penalty) |
| 1113 | { |
| 1114 | HudResult *result; |
| 1115 | |
| 1116 | g_return_val_if_fail (HUD_IS_ITEM (item), NULL); |
| 1117 | - g_return_val_if_fail (search_string != NULL, NULL); |
| 1118 | + g_return_val_if_fail (search_tokens != NULL, NULL); |
| 1119 | |
| 1120 | result = g_object_new (HUD_TYPE_RESULT, NULL); |
| 1121 | result->item = g_object_ref (item); |
| 1122 | - result->distance = calculate_distance_from_list (search_string, hud_item_get_tokens (item), &result->matched); |
| 1123 | + result->distance = hud_token_list_distance (hud_item_get_token_list (item), search_tokens, &result->matched); |
| 1124 | hud_result_format_description (result); |
| 1125 | |
| 1126 | result->distance += (result->distance * penalty) / 100; |
| 1127 | |
| 1128 | === modified file 'src/hudresult.h' |
| 1129 | --- src/hudresult.h 2012-03-05 04:46:53 +0000 |
| 1130 | +++ src/hudresult.h 2012-04-03 12:30:26 +0000 |
| 1131 | @@ -31,18 +31,18 @@ |
| 1132 | |
| 1133 | GType hud_result_get_type (void); |
| 1134 | |
| 1135 | -HudResult * hud_result_new (HudItem *item, |
| 1136 | - const gchar *search_string, |
| 1137 | - guint penalty); |
| 1138 | - |
| 1139 | -HudResult * hud_result_get_if_matched (HudItem *item, |
| 1140 | - const gchar *search_string, |
| 1141 | - guint penalty); |
| 1142 | - |
| 1143 | -HudItem * hud_result_get_item (HudResult *result); |
| 1144 | -guint hud_result_get_distance (HudResult *result, |
| 1145 | - guint max_usage); |
| 1146 | -const gchar * hud_result_get_html_description (HudResult *result); |
| 1147 | +HudResult * hud_result_new (HudItem *item, |
| 1148 | + HudTokenList *search_tokens, |
| 1149 | + guint penalty); |
| 1150 | + |
| 1151 | +HudResult * hud_result_get_if_matched (HudItem *item, |
| 1152 | + HudTokenList *search_tokens, |
| 1153 | + guint penalty); |
| 1154 | + |
| 1155 | +HudItem * hud_result_get_item (HudResult *result); |
| 1156 | +guint hud_result_get_distance (HudResult *result, |
| 1157 | + guint max_usage); |
| 1158 | +const gchar * hud_result_get_html_description (HudResult *result); |
| 1159 | |
| 1160 | |
| 1161 | #endif /* __HUD_RESULT_H__ */ |
| 1162 | |
| 1163 | === modified file 'src/hudsettings.c' |
| 1164 | --- src/hudsettings.c 2012-03-07 04:11:28 +0000 |
| 1165 | +++ src/hudsettings.c 2012-04-03 12:30:26 +0000 |
| 1166 | @@ -53,20 +53,14 @@ |
| 1167 | * matching result |
| 1168 | * @add_penalty: the penalty incurred by a character in the search term |
| 1169 | * that does not exist in the item being matched |
| 1170 | - * @add_penalty_pre: the penalty incurred by a character in the search |
| 1171 | - * term that does not exist in the item being matched when that |
| 1172 | - * character comes at the beginning of the term |
| 1173 | * @drop_penalty: the penalty incurred by a character missing from the |
| 1174 | * search string as compared to the item being matched |
| 1175 | - * @drop_penalty_end: the penalty incurred by a character missing from |
| 1176 | + * @end_drop_penalty: the penalty incurred by a character missing from |
| 1177 | * the search string as compared to the item being matched when the |
| 1178 | * character is at the end of the item (ie: the search term is a |
| 1179 | * prefix of the item) |
| 1180 | - * @transpose_penalty: the penalty incurred for transposed characters |
| 1181 | * @swap_penalty: the penalty incurred for the substitution of one |
| 1182 | * character for another |
| 1183 | - * @swap_penalty_case: the penalty incurred for the substitution of one |
| 1184 | - * character for another when the characters differ only in case |
| 1185 | * |
| 1186 | * This structure contains the value of several tunable parameters that |
| 1187 | * affect the behaviour of various components of the HUD. |
| 1188 | @@ -101,19 +95,14 @@ |
| 1189 | hud_settings.indicator_penalty = g_settings_get_uint (settings, "indicator-penalty"); |
| 1190 | hud_settings.max_distance = g_settings_get_uint (settings, "max-distance"); |
| 1191 | hud_settings.add_penalty = g_settings_get_uint (settings, "add-penalty"); |
| 1192 | - hud_settings.add_penalty_pre = g_settings_get_uint (settings, "add-penalty-pre"); |
| 1193 | hud_settings.drop_penalty = g_settings_get_uint (settings, "drop-penalty"); |
| 1194 | - hud_settings.drop_penalty_end = g_settings_get_uint (settings, "drop-penalty-end"); |
| 1195 | - hud_settings.transpose_penalty = g_settings_get_uint (settings, "transpose-penalty"); |
| 1196 | + hud_settings.end_drop_penalty = g_settings_get_uint (settings, "end-drop-penalty"); |
| 1197 | hud_settings.swap_penalty = g_settings_get_uint (settings, "swap-penalty"); |
| 1198 | - hud_settings.swap_penalty_case = g_settings_get_uint (settings, "swap-penalty-case"); |
| 1199 | |
| 1200 | g_debug ("indicator penalty: %u, max distance: %u", |
| 1201 | hud_settings.indicator_penalty, hud_settings.max_distance); |
| 1202 | - g_debug ("penalties: add:%u add-pre:%u drop:%u drop-end:%u trans:%u swap:%u swap-case:%u", |
| 1203 | - hud_settings.add_penalty, hud_settings.add_penalty_pre, hud_settings.drop_penalty, |
| 1204 | - hud_settings.drop_penalty_end, hud_settings.transpose_penalty, hud_settings.swap_penalty, |
| 1205 | - hud_settings.swap_penalty_case); |
| 1206 | + g_debug ("penalties: add:%u drop:%u end-drop:%u swap:%u", hud_settings.add_penalty, |
| 1207 | + hud_settings.drop_penalty, hud_settings.end_drop_penalty, hud_settings.swap_penalty); |
| 1208 | } |
| 1209 | |
| 1210 | /** |
| 1211 | |
| 1212 | === modified file 'src/hudsettings.h' |
| 1213 | --- src/hudsettings.h 2012-03-07 04:11:28 +0000 |
| 1214 | +++ src/hudsettings.h 2012-04-03 12:30:26 +0000 |
| 1215 | @@ -31,12 +31,9 @@ |
| 1216 | guint max_distance; |
| 1217 | |
| 1218 | guint add_penalty; |
| 1219 | - guint add_penalty_pre; |
| 1220 | guint drop_penalty; |
| 1221 | - guint drop_penalty_end; |
| 1222 | - guint transpose_penalty; |
| 1223 | + guint end_drop_penalty; |
| 1224 | guint swap_penalty; |
| 1225 | - guint swap_penalty_case; |
| 1226 | }; |
| 1227 | |
| 1228 | extern HudSettings hud_settings; |
| 1229 | |
| 1230 | === modified file 'src/hudsource.c' |
| 1231 | --- src/hudsource.c 2012-03-15 18:25:50 +0000 |
| 1232 | +++ src/hudsource.c 2012-04-03 12:30:26 +0000 |
| 1233 | @@ -144,9 +144,9 @@ |
| 1234 | * function may have changed, at which point you should call it again. |
| 1235 | **/ |
| 1236 | void |
| 1237 | -hud_source_search (HudSource *source, |
| 1238 | - GPtrArray *results_array, |
| 1239 | - const gchar *search_string) |
| 1240 | +hud_source_search (HudSource *source, |
| 1241 | + GPtrArray *results_array, |
| 1242 | + HudTokenList *search_string) |
| 1243 | { |
| 1244 | g_debug ("search on %s %p", G_OBJECT_TYPE_NAME (source), source); |
| 1245 | |
| 1246 | |
| 1247 | === modified file 'src/hudsource.h' |
| 1248 | --- src/hudsource.h 2012-03-15 18:25:50 +0000 |
| 1249 | +++ src/hudsource.h 2012-04-03 12:30:26 +0000 |
| 1250 | @@ -36,22 +36,22 @@ |
| 1251 | { |
| 1252 | GTypeInterface g_iface; |
| 1253 | |
| 1254 | - void (* use) (HudSource *source); |
| 1255 | - void (* unuse) (HudSource *source); |
| 1256 | - void (* search) (HudSource *source, |
| 1257 | - GPtrArray *results_array, |
| 1258 | - const gchar *search_string); |
| 1259 | + void (* use) (HudSource *source); |
| 1260 | + void (* unuse) (HudSource *source); |
| 1261 | + void (* search) (HudSource *source, |
| 1262 | + GPtrArray *results_array, |
| 1263 | + HudTokenList *search_tokens); |
| 1264 | }; |
| 1265 | |
| 1266 | GType hud_source_get_type (void); |
| 1267 | |
| 1268 | -void hud_source_use (HudSource *source); |
| 1269 | -void hud_source_unuse (HudSource *source); |
| 1270 | - |
| 1271 | -void hud_source_search (HudSource *source, |
| 1272 | - GPtrArray *results_array, |
| 1273 | - const gchar *search_string); |
| 1274 | - |
| 1275 | -void hud_source_changed (HudSource *source); |
| 1276 | +void hud_source_use (HudSource *source); |
| 1277 | +void hud_source_unuse (HudSource *source); |
| 1278 | + |
| 1279 | +void hud_source_search (HudSource *source, |
| 1280 | + GPtrArray *results_array, |
| 1281 | + HudTokenList *search_tokens); |
| 1282 | + |
| 1283 | +void hud_source_changed (HudSource *source); |
| 1284 | |
| 1285 | #endif /* __HUD_SOURCE_H__ */ |
| 1286 | |
| 1287 | === modified file 'src/hudsourcelist.c' |
| 1288 | --- src/hudsourcelist.c 2012-03-15 18:25:50 +0000 |
| 1289 | +++ src/hudsourcelist.c 2012-04-03 12:30:26 +0000 |
| 1290 | @@ -83,9 +83,9 @@ |
| 1291 | } |
| 1292 | |
| 1293 | static void |
| 1294 | -hud_source_list_search (HudSource *source, |
| 1295 | - GPtrArray *results_array, |
| 1296 | - const gchar *search_string) |
| 1297 | +hud_source_list_search (HudSource *source, |
| 1298 | + GPtrArray *results_array, |
| 1299 | + HudTokenList *search_string) |
| 1300 | { |
| 1301 | HudSourceList *list = HUD_SOURCE_LIST (source); |
| 1302 | GSList *node; |
| 1303 | |
| 1304 | === added file 'src/hudtoken.c' |
| 1305 | --- src/hudtoken.c 1970-01-01 00:00:00 +0000 |
| 1306 | +++ src/hudtoken.c 2012-04-03 12:30:26 +0000 |
| 1307 | @@ -0,0 +1,394 @@ |
| 1308 | +/* |
| 1309 | + * Copyright © 2012 Canonical Ltd. |
| 1310 | + * |
| 1311 | + * This program is free software: you can redistribute it and/or modify it |
| 1312 | + * under the terms of the GNU General Public License version 3, as |
| 1313 | + * published by the Free Software Foundation. |
| 1314 | + * |
| 1315 | + * This program is distributed in the hope that it will be useful, but |
| 1316 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
| 1317 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
| 1318 | + * PURPOSE. See the GNU General Public License for more details. |
| 1319 | + * |
| 1320 | + * You should have received a copy of the GNU General Public License along |
| 1321 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
| 1322 | + * |
| 1323 | + * Author: Ryan Lortie <desrt@desrt.ca> |
| 1324 | + */ |
| 1325 | + |
| 1326 | +#include "hudtoken.h" |
| 1327 | + |
| 1328 | +#include "hudsettings.h" |
| 1329 | + |
| 1330 | +#include <string.h> |
| 1331 | + |
| 1332 | +struct _HudToken |
| 1333 | +{ |
| 1334 | + guint length; |
| 1335 | + gchar *original; |
| 1336 | + gunichar *str; |
| 1337 | +}; |
| 1338 | + |
| 1339 | +/* This is actually one greater than the max-length. |
| 1340 | + * Should be power-of-two for best performance. |
| 1341 | + */ |
| 1342 | +#define TOKEN_LENGTH_LIMIT 32 |
| 1343 | + |
| 1344 | +#if 0 |
| 1345 | +static void |
| 1346 | +hud_token_distance_debug (const HudToken *haystack, |
| 1347 | + const HudToken *needle, |
| 1348 | + guint d[TOKEN_LENGTH_LIMIT][TOKEN_LENGTH_LIMIT]) |
| 1349 | +{ |
| 1350 | + gint i, j; |
| 1351 | + |
| 1352 | + g_print (" "); |
| 1353 | + for (j = 0; j <= haystack->length; j++) |
| 1354 | + g_print ("%6lc", (wchar_t) (j ? haystack->str[j - 1] : ' ')); |
| 1355 | + g_print ("\n"); |
| 1356 | + |
| 1357 | + for (i = 0; i <= needle->length; i++) |
| 1358 | + { |
| 1359 | + g_print ("%lc", (wchar_t) (i ? needle->str[i - 1] : ' ')); |
| 1360 | + for (j = 0; j <= haystack->length; j++) |
| 1361 | + g_print ("%6u", d[i][j]); |
| 1362 | + g_print ("\n"); |
| 1363 | + } |
| 1364 | +} |
| 1365 | +#endif |
| 1366 | + |
| 1367 | +#define MIN3(a,b,c) (MIN(MIN((a),(b)),(c))) |
| 1368 | + |
| 1369 | +/* Keeping in mind the fact that we're matching single words against |
| 1370 | + * single words, we can expect the extremely vast majority of cases to |
| 1371 | + * see needle and haystack both be less than 32 characters in length. |
| 1372 | + * |
| 1373 | + * For the common case we can avoid memory allocation by using a static |
| 1374 | + * array. By making the array a constant factor-of-two size we can |
| 1375 | + * replace multiplication by a variable with bitshift by a constant. |
| 1376 | + * |
| 1377 | + * Tokens longer than this are expected to be so unlikely that we |
| 1378 | + * simply deal with them by truncation during the normalisation phase. |
| 1379 | + */ |
| 1380 | +guint |
| 1381 | +hud_token_distance (const HudToken *haystack, |
| 1382 | + const HudToken *needle) |
| 1383 | +{ |
| 1384 | + static guint d[TOKEN_LENGTH_LIMIT][TOKEN_LENGTH_LIMIT]; |
| 1385 | + gunichar h, n; |
| 1386 | + gint result; |
| 1387 | + gint i, j; |
| 1388 | + |
| 1389 | + g_assert (haystack->length < TOKEN_LENGTH_LIMIT && needle->length < TOKEN_LENGTH_LIMIT); |
| 1390 | + g_assert (haystack->str[haystack->length] == 0); |
| 1391 | + g_assert (needle->str[needle->length] == 0); |
| 1392 | + |
| 1393 | + /* This function only performs memory writes to 'd' and calls no other |
| 1394 | + * functions. No pointer to 'd' is ever leaked. Hopefully the |
| 1395 | + * compiler is clever enough to therefore realise that no other memory |
| 1396 | + * will be modified during the running of this function and optimise |
| 1397 | + * aggressively. |
| 1398 | + */ |
| 1399 | + |
| 1400 | + /* By convention we refer to "add" and "drop" in terms of "mistakes |
| 1401 | + * the user made". The user's token is the one in "needle". To give |
| 1402 | + * examples against the menu item "preferences", if the user typed: |
| 1403 | + * |
| 1404 | + * - "prefereces": this has a dropped character |
| 1405 | + * |
| 1406 | + * - "prefferences": this has an added character |
| 1407 | + * |
| 1408 | + * - "prefefences": this has a swap |
| 1409 | + * |
| 1410 | + * We organise the matrix with each column (j) corresponding to a |
| 1411 | + * character in the menu item text ('haystack') and each row (i) |
| 1412 | + * corresponding to a character in the search token ('needle'). |
| 1413 | + * |
| 1414 | + * We modify the Levenshtein algorithm in the following ways: |
| 1415 | + * |
| 1416 | + * - configurable penalties for various mistakes (add, drop, |
| 1417 | + * swap). This is done by replacing additions of '1' with |
| 1418 | + * additions of these configurable values. |
| 1419 | + * |
| 1420 | + * - a lower penalties for drops that occur at the end of the token |
| 1421 | + * that the user typed. For example, "prefer" would be given a |
| 1422 | + * lower penalty for those 5 missing letters than would occur if |
| 1423 | + * they were not missing from the end. |
| 1424 | + * |
| 1425 | + * This is done by special-casing the last row (i == nlen) in the |
| 1426 | + * matrix, corresponding to the last character in the search |
| 1427 | + * token. In this case, we calculate drops at a lower penalty. |
| 1428 | + * |
| 1429 | + * Implementing the first of these two changes is a simple tweak: |
| 1430 | + * instead of adding '1', add the configurable value. |
| 1431 | + * |
| 1432 | + * Implementing the second one is somewhat more difficult: we could |
| 1433 | + * modify the core algorithm, but that would violate the 'pureness' of |
| 1434 | + * the dynamic programming approach. |
| 1435 | + * |
| 1436 | + * Instead, we wait until the 'pure' algorithm is done then instead of |
| 1437 | + * only considering the result for the full menu item text (ie: the |
| 1438 | + * number in the bottom right corner) we consider all prefixes of the |
| 1439 | + * menu item text by scanning the entire bottom row (and adding a |
| 1440 | + * penalty according to the number of characters removed). |
| 1441 | + */ |
| 1442 | + |
| 1443 | + /* http://en.wikipedia.org/wiki/Levenshtein_distance#Computing_Levenshtein_distance */ |
| 1444 | + for (i = 0; i <= needle->length; i++) |
| 1445 | + d[i][0] = i * hud_settings.add_penalty; |
| 1446 | + |
| 1447 | + for (j = 0; j <= haystack->length; j++) |
| 1448 | + d[0][j] = j * hud_settings.drop_penalty; |
| 1449 | + |
| 1450 | + for (i = 1; (n = needle->str[i - 1]); i++) |
| 1451 | + for (j = 1; (h = haystack->str[j - 1]); j++) |
| 1452 | + if (n == h) |
| 1453 | + d[i][j] = d[i - 1][j - 1]; |
| 1454 | + |
| 1455 | + else |
| 1456 | + d[i][j] = MIN3(d[i - 1][j - 1] + hud_settings.swap_penalty, |
| 1457 | + d[i ][j - 1] + hud_settings.drop_penalty, |
| 1458 | + d[i - 1][j ] + hud_settings.add_penalty); |
| 1459 | + |
| 1460 | + /* Noe we consider all prefixes of the menu item text to discover |
| 1461 | + * which one gives us the best score. |
| 1462 | + * |
| 1463 | + * If we end up picking a result from a column other than the |
| 1464 | + * rightmost, it will have had the correct multiple of the end-drop |
| 1465 | + * penalty added to it by the time we're done. |
| 1466 | + */ |
| 1467 | + result = d[--i][0]; |
| 1468 | + for (j = 1; j <= haystack->length; j++) |
| 1469 | + result = MIN (d[i][j], result + hud_settings.end_drop_penalty); |
| 1470 | + |
| 1471 | + return result; |
| 1472 | +} |
| 1473 | + |
| 1474 | +HudToken * |
| 1475 | +hud_token_new (const gchar *str, |
| 1476 | + gssize length) |
| 1477 | +{ |
| 1478 | + HudToken *token; |
| 1479 | + gchar *normal; |
| 1480 | + gchar *folded; |
| 1481 | + glong items; |
| 1482 | + |
| 1483 | + token = g_slice_new (HudToken); |
| 1484 | + |
| 1485 | + token->original = g_strndup (str, length); |
| 1486 | + normal = g_utf8_normalize (str, length, G_NORMALIZE_ALL); |
| 1487 | + folded = g_utf8_casefold (normal, -1); |
| 1488 | + token->str = g_utf8_to_ucs4_fast (folded, -1, &items); |
| 1489 | + token->length = items; |
| 1490 | + g_free (folded); |
| 1491 | + g_free (normal); |
| 1492 | + |
| 1493 | + if (!(token->length < TOKEN_LENGTH_LIMIT)) |
| 1494 | + { |
| 1495 | + token->length = 31; |
| 1496 | + token->str[31] = 0; |
| 1497 | + } |
| 1498 | + |
| 1499 | + return token; |
| 1500 | +} |
| 1501 | + |
| 1502 | +void |
| 1503 | +hud_token_free (HudToken *token) |
| 1504 | +{ |
| 1505 | + g_free (token->original); |
| 1506 | + g_free (token->str); |
| 1507 | + g_slice_free (HudToken, token); |
| 1508 | +} |
| 1509 | + |
| 1510 | +static const gchar * |
| 1511 | +hud_token_get_original (HudToken *token) |
| 1512 | +{ |
| 1513 | + return token->original; |
| 1514 | +} |
| 1515 | + |
| 1516 | +struct _HudTokenList |
| 1517 | +{ |
| 1518 | + HudToken **tokens; |
| 1519 | + gint length; |
| 1520 | +}; |
| 1521 | + |
| 1522 | +static HudTokenList * |
| 1523 | +hud_token_list_new_consume_array (GPtrArray *array) |
| 1524 | +{ |
| 1525 | + HudTokenList *list; |
| 1526 | + |
| 1527 | + list = g_slice_new (HudTokenList); |
| 1528 | + list->tokens = (HudToken **) array->pdata; |
| 1529 | + list->length = array->len; |
| 1530 | + |
| 1531 | + g_ptr_array_free (array, FALSE); |
| 1532 | + |
| 1533 | + return list; |
| 1534 | +} |
| 1535 | + |
| 1536 | +#define SEPARATORS " .->" |
| 1537 | +static void |
| 1538 | +hud_token_list_add_string_to_array (GPtrArray *array, |
| 1539 | + const gchar *string) |
| 1540 | +{ |
| 1541 | + while (*string) |
| 1542 | + { |
| 1543 | + /* strip separators */ |
| 1544 | + string += strspn (string, SEPARATORS); |
| 1545 | + |
| 1546 | + if (*string) |
| 1547 | + { |
| 1548 | + gint length; |
| 1549 | + |
| 1550 | + /* consume a token */ |
| 1551 | + length = strcspn (string, SEPARATORS); |
| 1552 | + g_ptr_array_add (array, hud_token_new (string, length)); |
| 1553 | + string += length; |
| 1554 | + } |
| 1555 | + } |
| 1556 | +} |
| 1557 | + |
| 1558 | +static void |
| 1559 | +hud_token_list_add_string_list_to_array (GPtrArray *array, |
| 1560 | + HudStringList *list) |
| 1561 | +{ |
| 1562 | + if (list == NULL) |
| 1563 | + return; |
| 1564 | + |
| 1565 | + hud_token_list_add_string_list_to_array (array, hud_string_list_get_tail (list)); |
| 1566 | + hud_token_list_add_string_to_array (array, hud_string_list_get_head (list)); |
| 1567 | +} |
| 1568 | + |
| 1569 | +HudTokenList * |
| 1570 | +hud_token_list_new_from_string (const gchar *string) |
| 1571 | +{ |
| 1572 | + GPtrArray *array; |
| 1573 | + |
| 1574 | + array = g_ptr_array_new (); |
| 1575 | + hud_token_list_add_string_to_array (array, string); |
| 1576 | + return hud_token_list_new_consume_array (array); |
| 1577 | +} |
| 1578 | + |
| 1579 | +HudTokenList * |
| 1580 | +hud_token_list_new_from_string_list (HudStringList *string_list) |
| 1581 | +{ |
| 1582 | + GPtrArray *array; |
| 1583 | + |
| 1584 | + array = g_ptr_array_new (); |
| 1585 | + hud_token_list_add_string_list_to_array (array, string_list); |
| 1586 | + return hud_token_list_new_consume_array (array); |
| 1587 | +} |
| 1588 | + |
| 1589 | +void |
| 1590 | +hud_token_list_free (HudTokenList *list) |
| 1591 | +{ |
| 1592 | + gint i; |
| 1593 | + |
| 1594 | + for (i = 0; i < list->length; i++) |
| 1595 | + hud_token_free (list->tokens[i]); |
| 1596 | + |
| 1597 | + g_free (list->tokens); |
| 1598 | + |
| 1599 | + g_slice_free (HudTokenList, list); |
| 1600 | +} |
| 1601 | + |
| 1602 | +guint |
| 1603 | +hud_token_list_distance (HudTokenList *haystack, |
| 1604 | + HudTokenList *needle, |
| 1605 | + const gchar ***matches) |
| 1606 | +{ |
| 1607 | + static guint d[32][32]; |
| 1608 | + gint i, j; |
| 1609 | + |
| 1610 | + if (needle->length > haystack->length) |
| 1611 | + return G_MAXUINT; |
| 1612 | + |
| 1613 | + /* Simply refuse to deal with ridiculous situations. |
| 1614 | + * |
| 1615 | + * This only happens when the user has more than 32 words in their |
| 1616 | + * search or the same happens in a menu item. |
| 1617 | + */ |
| 1618 | + if (haystack->length > 32 || needle->length > 32) |
| 1619 | + return G_MAXUINT; |
| 1620 | + |
| 1621 | + /* unroll the handling of the first needle term */ |
| 1622 | + { |
| 1623 | + guint cost; |
| 1624 | + |
| 1625 | + /* unroll the handling of the first haystack term */ |
| 1626 | + cost = hud_token_distance (haystack->tokens[0], needle->tokens[0]); |
| 1627 | + d[0][0] = cost; |
| 1628 | + |
| 1629 | + for (j = 0; j < haystack->length; j++) |
| 1630 | + { |
| 1631 | + guint take_cost; |
| 1632 | + |
| 1633 | + take_cost = hud_token_distance (haystack->tokens[j], needle->tokens[0]); |
| 1634 | + cost = MIN (take_cost, cost + 1); |
| 1635 | + d[0][j] = cost; |
| 1636 | + } |
| 1637 | + } |
| 1638 | + |
| 1639 | + /* if we have only one needle, this loop won't run at all */ |
| 1640 | + for (i = 1; i < needle->length; i++) |
| 1641 | + { |
| 1642 | + guint cost; |
| 1643 | + |
| 1644 | + /* unroll the handling of the first haystack term */ |
| 1645 | + cost = d[i - 1][i - 1] + hud_token_distance (haystack->tokens[i], needle->tokens[i]); |
| 1646 | + d[i][i] = cost; |
| 1647 | + |
| 1648 | + for (j = i + 1; j < haystack->length; j++) |
| 1649 | + { |
| 1650 | + guint prev_cost; |
| 1651 | + |
| 1652 | + prev_cost = d[i - 1][j - 1]; |
| 1653 | + /* Only do checking of additional terms of it's possible that |
| 1654 | + * we'll come in under the max distance AND beat the cost of |
| 1655 | + * just dropping the term. |
| 1656 | + * |
| 1657 | + * hud_token_distance() could return zero so we need to try it |
| 1658 | + * if: |
| 1659 | + * |
| 1660 | + * - prev_cost is less than or equal to the max distance |
| 1661 | + * (because then our result could equal the max distance) |
| 1662 | + * |
| 1663 | + * - prev_cost is less than or equal to the last cost |
| 1664 | + * Even if it's equal, skipping has a cost and a perfect |
| 1665 | + * match has no cost, so we need to try the match. |
| 1666 | + */ |
| 1667 | + if (prev_cost <= hud_settings.max_distance && prev_cost <= cost) |
| 1668 | + { |
| 1669 | + guint take_cost; |
| 1670 | + |
| 1671 | + take_cost = prev_cost + hud_token_distance (haystack->tokens[j], needle->tokens[i]); |
| 1672 | + cost = MIN (take_cost, cost + 1); |
| 1673 | + } |
| 1674 | + else |
| 1675 | + cost = cost + 1; |
| 1676 | + |
| 1677 | + d[i][j] = cost; |
| 1678 | + } |
| 1679 | + } |
| 1680 | + |
| 1681 | + /* Discover which terms were matched */ |
| 1682 | + if (matches) |
| 1683 | + { |
| 1684 | + *matches = g_new (const gchar *, needle->length + 1); |
| 1685 | + |
| 1686 | + j = haystack->length - 1; |
| 1687 | + |
| 1688 | + for (i = needle->length - 1; i >= 0; i--) |
| 1689 | + { |
| 1690 | + while (j > i && d[i][j-1] == d[i][j] - 1) |
| 1691 | + j--; |
| 1692 | + |
| 1693 | + (*matches)[i] = hud_token_get_original (haystack->tokens[j]); |
| 1694 | + j--; |
| 1695 | + } |
| 1696 | + |
| 1697 | + (*matches)[needle->length] = NULL; |
| 1698 | + } |
| 1699 | + |
| 1700 | + return d[needle->length - 1][haystack->length - 1]; |
| 1701 | +} |
| 1702 | |
| 1703 | === added file 'src/hudtoken.h' |
| 1704 | --- src/hudtoken.h 1970-01-01 00:00:00 +0000 |
| 1705 | +++ src/hudtoken.h 2012-04-03 12:30:26 +0000 |
| 1706 | @@ -0,0 +1,40 @@ |
| 1707 | +/* |
| 1708 | + * Copyright © 2012 Canonical Ltd. |
| 1709 | + * |
| 1710 | + * This program is free software: you can redistribute it and/or modify it |
| 1711 | + * under the terms of the GNU General Public License version 3, as |
| 1712 | + * published by the Free Software Foundation. |
| 1713 | + * |
| 1714 | + * This program is distributed in the hope that it will be useful, but |
| 1715 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
| 1716 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
| 1717 | + * PURPOSE. See the GNU General Public License for more details. |
| 1718 | + * |
| 1719 | + * You should have received a copy of the GNU General Public License along |
| 1720 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
| 1721 | + * |
| 1722 | + * Author: Ryan Lortie <desrt@desrt.ca> |
| 1723 | + */ |
| 1724 | + |
| 1725 | +#ifndef __HUD_TOKEN_H__ |
| 1726 | +#define __HUD_TOKEN_H__ |
| 1727 | + |
| 1728 | +#include "hudstringlist.h" |
| 1729 | + |
| 1730 | +typedef struct _HudTokenList HudTokenList; |
| 1731 | +typedef struct _HudToken HudToken; |
| 1732 | + |
| 1733 | +HudToken * hud_token_new (const gchar *token, |
| 1734 | + gssize length); |
| 1735 | +void hud_token_free (HudToken *token); |
| 1736 | +guint hud_token_distance (const HudToken *haystack, |
| 1737 | + const HudToken *needle); |
| 1738 | + |
| 1739 | +HudTokenList * hud_token_list_new_from_string (const gchar *string); |
| 1740 | +HudTokenList * hud_token_list_new_from_string_list (HudStringList *string_list); |
| 1741 | +void hud_token_list_free (HudTokenList *list); |
| 1742 | +guint hud_token_list_distance (HudTokenList *haystack, |
| 1743 | + HudTokenList *needle, |
| 1744 | + const gchar ***matches); |
| 1745 | + |
| 1746 | +#endif /* __HUD_TOKEN_H__ */ |
| 1747 | |
| 1748 | === modified file 'src/hudwindowsource.c' |
| 1749 | --- src/hudwindowsource.c 2012-03-23 15:35:01 +0000 |
| 1750 | +++ src/hudwindowsource.c 2012-04-03 12:30:26 +0000 |
| 1751 | @@ -67,7 +67,7 @@ |
| 1752 | BamfWindow *active_window; |
| 1753 | BamfApplication *active_application; |
| 1754 | const gchar *active_desktop_file; |
| 1755 | - const gchar *active_icon; |
| 1756 | + gchar *active_icon; |
| 1757 | HudSource *active_collector; |
| 1758 | gint use_count; |
| 1759 | }; |
| 1760 | @@ -255,6 +255,7 @@ |
| 1761 | g_clear_object (&source->active_collector); |
| 1762 | g_clear_object (&source->active_application); |
| 1763 | g_clear_object (&source->active_window); |
| 1764 | + g_free (source->active_icon); |
| 1765 | source->active_window = g_object_ref (window); |
| 1766 | source->active_application = g_object_ref (application); |
| 1767 | source->active_desktop_file = desktop_file; |
| 1768 | @@ -296,9 +297,9 @@ |
| 1769 | } |
| 1770 | |
| 1771 | static void |
| 1772 | -hud_window_source_search (HudSource *hud_source, |
| 1773 | - GPtrArray *results_array, |
| 1774 | - const gchar *search_string) |
| 1775 | +hud_window_source_search (HudSource *hud_source, |
| 1776 | + GPtrArray *results_array, |
| 1777 | + HudTokenList *search_string) |
| 1778 | { |
| 1779 | HudWindowSource *source = HUD_WINDOW_SOURCE (hud_source); |
| 1780 | |
| 1781 | @@ -311,9 +312,13 @@ |
| 1782 | { |
| 1783 | HudWindowSource *source = HUD_WINDOW_SOURCE (object); |
| 1784 | |
| 1785 | + g_assert_cmpint (source->use_count, ==, 0); |
| 1786 | + |
| 1787 | /* bamf matcher signals already disconnected in dispose */ |
| 1788 | + g_clear_object (&source->active_collector); |
| 1789 | g_clear_object (&source->active_application); |
| 1790 | g_clear_object (&source->active_window); |
| 1791 | + g_free (source->active_icon); |
| 1792 | |
| 1793 | G_OBJECT_CLASS (hud_window_source_parent_class) |
| 1794 | ->finalize (object); |
| 1795 | |
| 1796 | === modified file 'tests/Makefile.am' |
| 1797 | --- tests/Makefile.am 2012-03-27 16:38:41 +0000 |
| 1798 | +++ tests/Makefile.am 2012-04-03 12:30:26 +0000 |
| 1799 | @@ -391,6 +391,24 @@ |
| 1800 | |
| 1801 | DISTCLEANFILES += test-dbus-message-count.bustle |
| 1802 | |
| 1803 | +################### |
| 1804 | +# Test Performance |
| 1805 | +################### |
| 1806 | + |
| 1807 | +check_PROGRAMS += \ |
| 1808 | + hud-performance |
| 1809 | + |
| 1810 | +hud_performance_CFLAGS = \ |
| 1811 | + -I$(top_builddir)/src \ |
| 1812 | + -I$(top_srcdir)/src \ |
| 1813 | + $(HUD_CFLAGS) |
| 1814 | +hud_performance_LDADD = \ |
| 1815 | + ../src/libhud-service.a \ |
| 1816 | + $(HUD_LIBS) |
| 1817 | +hud_performance_SOURCES = \ |
| 1818 | + hud-performance.c \ |
| 1819 | + word-list.h |
| 1820 | + |
| 1821 | ######################### |
| 1822 | # Footer |
| 1823 | ######################### |
| 1824 | |
| 1825 | === added file 'tests/hud-performance.c' |
| 1826 | --- tests/hud-performance.c 1970-01-01 00:00:00 +0000 |
| 1827 | +++ tests/hud-performance.c 2012-04-03 12:30:26 +0000 |
| 1828 | @@ -0,0 +1,279 @@ |
| 1829 | +/* |
| 1830 | + * Copyright © 2012 Canonical Ltd. |
| 1831 | + * |
| 1832 | + * This program is free software: you can redistribute it and/or modify it |
| 1833 | + * under the terms of the GNU General Public License version 3, as |
| 1834 | + * published by the Free Software Foundation. |
| 1835 | + * |
| 1836 | + * This program is distributed in the hope that it will be useful, but |
| 1837 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
| 1838 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
| 1839 | + * PURPOSE. See the GNU General Public License for more details. |
| 1840 | + * |
| 1841 | + * You should have received a copy of the GNU General Public License along |
| 1842 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
| 1843 | + * |
| 1844 | + * Author: Ryan Lortie <desrt@desrt.ca> |
| 1845 | + */ |
| 1846 | + |
| 1847 | +#include "hudsettings.h" |
| 1848 | +#include "hudquery.h" |
| 1849 | +#include "hudtoken.h" |
| 1850 | +#include "hudsource.h" |
| 1851 | + |
| 1852 | +#include <glib-object.h> |
| 1853 | + |
| 1854 | +#include "word-list.h" |
| 1855 | + |
| 1856 | +/* Max nested depth of menu items */ |
| 1857 | +#define MAX_DEPTH 6 |
| 1858 | + |
| 1859 | +/* Max number of items per submenu */ |
| 1860 | +#define MAX_ITEMS 20 |
| 1861 | + |
| 1862 | +/* Max number of words per label. |
| 1863 | + * NB: keep MAX_WORDS * MAX_DEPTH under 32 |
| 1864 | + */ |
| 1865 | +#define MAX_WORDS 4 |
| 1866 | + |
| 1867 | +/* Longest word in the word-list (upper bound) */ |
| 1868 | +#define MAX_LETTERS 20 |
| 1869 | + |
| 1870 | +/* hardcode some parameters for reasons of determinism. |
| 1871 | + */ |
| 1872 | +HudSettings hud_settings = { |
| 1873 | + .indicator_penalty = 50, |
| 1874 | + .add_penalty = 10, |
| 1875 | + .drop_penalty = 10, |
| 1876 | + .end_drop_penalty = 1, |
| 1877 | + .swap_penalty = 15, |
| 1878 | + .max_distance = 30 |
| 1879 | +}; |
| 1880 | + |
| 1881 | +typedef struct |
| 1882 | +{ |
| 1883 | + GObject object; |
| 1884 | + |
| 1885 | + GHashTable *items; |
| 1886 | +} RandomSource; |
| 1887 | + |
| 1888 | +typedef GObjectClass RandomSourceClass; |
| 1889 | + |
| 1890 | +static void random_source_iface_init (HudSourceInterface *iface); |
| 1891 | +G_DEFINE_TYPE_WITH_CODE (RandomSource, random_source, G_TYPE_OBJECT, |
| 1892 | + G_IMPLEMENT_INTERFACE (HUD_TYPE_SOURCE, random_source_iface_init)) |
| 1893 | + |
| 1894 | +static void |
| 1895 | +random_source_search (HudSource *hud_source, |
| 1896 | + GPtrArray *results_array, |
| 1897 | + HudTokenList *search_tokens) |
| 1898 | +{ |
| 1899 | + RandomSource *source = (RandomSource *) hud_source; |
| 1900 | + GHashTableIter iter; |
| 1901 | + gpointer item; |
| 1902 | + |
| 1903 | + g_hash_table_iter_init (&iter, source->items); |
| 1904 | + while (g_hash_table_iter_next (&iter, &item, NULL)) |
| 1905 | + { |
| 1906 | + HudResult *result; |
| 1907 | + |
| 1908 | + result = hud_result_get_if_matched (item, search_tokens, 0); |
| 1909 | + if (result) |
| 1910 | + g_ptr_array_add (results_array, result); |
| 1911 | + } |
| 1912 | +} |
| 1913 | + |
| 1914 | +static void |
| 1915 | +random_source_ignore_use (HudSource *source) |
| 1916 | +{ |
| 1917 | +} |
| 1918 | + |
| 1919 | +static gchar * |
| 1920 | +make_word (GRand *rand, |
| 1921 | + gchar *buffer) |
| 1922 | +{ |
| 1923 | + const gchar *word; |
| 1924 | + gint choice; |
| 1925 | + gint len; |
| 1926 | + |
| 1927 | + choice = g_rand_int_range (rand, 0, G_N_ELEMENTS (word_list)); |
| 1928 | + word = word_list[choice]; |
| 1929 | + |
| 1930 | + while (*word) |
| 1931 | + *buffer++ = *word++; |
| 1932 | + |
| 1933 | + return buffer; |
| 1934 | +} |
| 1935 | + |
| 1936 | +static gchar * |
| 1937 | +make_words (GRand *rand, |
| 1938 | + gint n_words) |
| 1939 | +{ |
| 1940 | + gchar *buffer; |
| 1941 | + gchar *ptr; |
| 1942 | + gint i; |
| 1943 | + |
| 1944 | + buffer = g_malloc ((MAX_LETTERS + 1) * n_words); |
| 1945 | + |
| 1946 | + ptr = buffer; |
| 1947 | + for (i = 0; i < n_words; i++) |
| 1948 | + { |
| 1949 | + if (i) |
| 1950 | + *ptr++ = ' '; |
| 1951 | + |
| 1952 | + ptr = make_word (rand, ptr); |
| 1953 | + } |
| 1954 | + |
| 1955 | + *ptr = '\0'; |
| 1956 | + |
| 1957 | + return buffer; |
| 1958 | +} |
| 1959 | + |
| 1960 | +static HudStringList * |
| 1961 | +random_source_make_name (GRand *rand, |
| 1962 | + HudStringList *context) |
| 1963 | +{ |
| 1964 | + HudStringList *name; |
| 1965 | + gchar *label; |
| 1966 | + |
| 1967 | + label = make_words (rand, g_rand_int_range (rand, 1, MAX_WORDS + 1)); |
| 1968 | + name = hud_string_list_cons (label, context); |
| 1969 | + g_free (label); |
| 1970 | + |
| 1971 | + return name; |
| 1972 | +} |
| 1973 | + |
| 1974 | +static void |
| 1975 | +random_source_populate_table (GRand *rand, |
| 1976 | + GHashTable *items, |
| 1977 | + HudStringList *context, |
| 1978 | + gint depth) |
| 1979 | +{ |
| 1980 | + gint n_items; |
| 1981 | + gint i; |
| 1982 | + |
| 1983 | + n_items = g_rand_int_range (rand, 1, MAX_ITEMS + 1); |
| 1984 | + |
| 1985 | + for (i = 0; i < n_items; i++) |
| 1986 | + { |
| 1987 | + HudStringList *name; |
| 1988 | + gboolean is_submenu; |
| 1989 | + HudItem *item; |
| 1990 | + |
| 1991 | + name = random_source_make_name (rand, context); |
| 1992 | + |
| 1993 | + if (depth != MAX_DEPTH) |
| 1994 | + /* Decrease the chances of a particular item being a submenu as we |
| 1995 | + * go deeper into the menu structure. |
| 1996 | + */ |
| 1997 | + is_submenu = g_rand_int_range (rand, 0, depth + 1) == 0; |
| 1998 | + else |
| 1999 | + /* At the maximum depth, prevent any items from being submenus. */ |
| 2000 | + is_submenu = FALSE; |
| 2001 | + |
| 2002 | + item = hud_item_new (name, NULL, NULL, !is_submenu); |
| 2003 | + g_hash_table_add (items, item); |
| 2004 | + |
| 2005 | + if (is_submenu) |
| 2006 | + random_source_populate_table (rand, items, name, depth + 1); |
| 2007 | + |
| 2008 | + hud_string_list_unref (name); |
| 2009 | + } |
| 2010 | +} |
| 2011 | + |
| 2012 | +static void |
| 2013 | +random_source_finalize (GObject *object) |
| 2014 | +{ |
| 2015 | + RandomSource *source = (RandomSource *) object; |
| 2016 | + |
| 2017 | + g_hash_table_unref (source->items); |
| 2018 | + |
| 2019 | + G_OBJECT_CLASS (random_source_parent_class) |
| 2020 | + ->finalize (object); |
| 2021 | +} |
| 2022 | + |
| 2023 | +static void |
| 2024 | +random_source_init (RandomSource *source) |
| 2025 | +{ |
| 2026 | + source->items = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL); |
| 2027 | +} |
| 2028 | + |
| 2029 | +static void |
| 2030 | +random_source_iface_init (HudSourceInterface *iface) |
| 2031 | +{ |
| 2032 | + iface->use = random_source_ignore_use; |
| 2033 | + iface->unuse = random_source_ignore_use; |
| 2034 | + iface->search = random_source_search; |
| 2035 | +} |
| 2036 | + |
| 2037 | +static void |
| 2038 | +random_source_class_init (RandomSourceClass *class) |
| 2039 | +{ |
| 2040 | + class->finalize = random_source_finalize; |
| 2041 | +} |
| 2042 | + |
| 2043 | +static HudSource * |
| 2044 | +random_source_new (GRand *rand) |
| 2045 | +{ |
| 2046 | + RandomSource *source; |
| 2047 | + |
| 2048 | + source = g_object_new (random_source_get_type (), NULL); |
| 2049 | + random_source_populate_table (rand, source->items, NULL, 0); |
| 2050 | + |
| 2051 | + return HUD_SOURCE (source); |
| 2052 | +} |
| 2053 | + |
| 2054 | +void |
| 2055 | +test_query_performance (void) |
| 2056 | +{ |
| 2057 | + HudSource *source; |
| 2058 | + HudQuery *query; |
| 2059 | + GRand *rand; |
| 2060 | + gint i; |
| 2061 | + |
| 2062 | + rand = g_rand_new_with_seed (1234); |
| 2063 | + source = random_source_new (rand); |
| 2064 | + |
| 2065 | + for (i = 1; i <= 6; i++) |
| 2066 | + { |
| 2067 | + guint64 start_time; |
| 2068 | + gchar *search; |
| 2069 | + gint j; |
| 2070 | + |
| 2071 | + g_print ("\n"); |
| 2072 | + |
| 2073 | + search = make_words (rand, i); |
| 2074 | + |
| 2075 | + /* simulate the user typing it in, one character at a time */ |
| 2076 | + for (j = 1; search[j - 1]; j++) |
| 2077 | + { |
| 2078 | + gchar *part_search = g_strndup (search, j); |
| 2079 | + |
| 2080 | + start_time = g_get_monotonic_time (); |
| 2081 | + query = hud_query_new (source, part_search, 1u<<30); |
| 2082 | + g_print ("%-60s: %dus (%d hits)\n", part_search, |
| 2083 | + (int) (g_get_monotonic_time () - start_time), |
| 2084 | + hud_query_get_n_results (query)); |
| 2085 | + hud_query_close (query); |
| 2086 | + g_object_unref (query); |
| 2087 | + g_free (part_search); |
| 2088 | + } |
| 2089 | + |
| 2090 | + g_free (search); |
| 2091 | + } |
| 2092 | + |
| 2093 | + g_object_unref (source); |
| 2094 | + g_rand_free (rand); |
| 2095 | +} |
| 2096 | + |
| 2097 | +int |
| 2098 | +main (int argc, char **argv) |
| 2099 | +{ |
| 2100 | + g_type_init (); |
| 2101 | + |
| 2102 | + g_test_init (&argc, &argv, NULL); |
| 2103 | + if (g_test_perf ()) |
| 2104 | + g_test_add_func ("/hud/query-performance", test_query_performance); |
| 2105 | + |
| 2106 | + return g_test_run (); |
| 2107 | +} |
| 2108 | |
| 2109 | === modified file 'tests/test-distance.c' |
| 2110 | --- tests/test-distance.c 2012-03-12 18:52:56 +0000 |
| 2111 | +++ tests/test-distance.c 2012-04-03 12:30:26 +0000 |
| 2112 | @@ -23,7 +23,7 @@ |
| 2113 | #include <glib-object.h> |
| 2114 | |
| 2115 | #include "hudsettings.h" |
| 2116 | -#include "distance.h" |
| 2117 | +#include "hudtoken.h" |
| 2118 | |
| 2119 | /* hardcode some parameters so the test doesn't fail if the user |
| 2120 | * has bogus things in GSettings. |
| 2121 | @@ -31,15 +31,54 @@ |
| 2122 | HudSettings hud_settings = { |
| 2123 | .indicator_penalty = 50, |
| 2124 | .add_penalty = 10, |
| 2125 | - .add_penalty_pre = 1, |
| 2126 | .drop_penalty = 10, |
| 2127 | - .drop_penalty_end = 10, |
| 2128 | - .transpose_penalty = 10, |
| 2129 | - .swap_penalty = 10, |
| 2130 | - .swap_penalty_case = 1, |
| 2131 | + .end_drop_penalty = 1, |
| 2132 | + .swap_penalty = 15, |
| 2133 | .max_distance = 30 |
| 2134 | }; |
| 2135 | |
| 2136 | +static guint |
| 2137 | +calculate_distance (const gchar *search, GStrv teststrings, gchar ***matches) |
| 2138 | +{ |
| 2139 | + HudStringList *list = NULL; |
| 2140 | + HudTokenList *haystack; |
| 2141 | + HudTokenList *needle; |
| 2142 | + guint distance; |
| 2143 | + gint i; |
| 2144 | + |
| 2145 | + if (search == NULL || teststrings == NULL) { |
| 2146 | + return G_MAXINT; |
| 2147 | + } |
| 2148 | + |
| 2149 | + for (i = 0; teststrings[i]; i++) { |
| 2150 | + HudStringList *tmp; |
| 2151 | + |
| 2152 | + tmp = hud_string_list_cons (teststrings[i], list); |
| 2153 | + hud_string_list_unref (list); |
| 2154 | + list = tmp; |
| 2155 | + } |
| 2156 | + |
| 2157 | + haystack = hud_token_list_new_from_string_list (list); |
| 2158 | + hud_string_list_unref (list); |
| 2159 | + |
| 2160 | + needle = hud_token_list_new_from_string (search); |
| 2161 | + distance = hud_token_list_distance (haystack, needle, (const gchar ***) matches); |
| 2162 | + |
| 2163 | + if (matches) { |
| 2164 | + /* These are owned by the tokenlists, so make copies |
| 2165 | + * before freeing. |
| 2166 | + */ |
| 2167 | + for (i = 0; (*matches)[i]; i++) { |
| 2168 | + (*matches)[i] = g_strdup ((*matches)[i]); |
| 2169 | + } |
| 2170 | + } |
| 2171 | + |
| 2172 | + hud_token_list_free (haystack); |
| 2173 | + hud_token_list_free (needle); |
| 2174 | + |
| 2175 | + return distance; |
| 2176 | +} |
| 2177 | + |
| 2178 | /* Ensure the base calculation works */ |
| 2179 | static void |
| 2180 | test_distance_base (void) |
| 2181 | @@ -185,6 +224,18 @@ |
| 2182 | return; |
| 2183 | } |
| 2184 | |
| 2185 | +/* Check to make sure 'Save' matches better than 'Save As...' for "save" */ |
| 2186 | +static void |
| 2187 | +test_distance_extra_terms (void) |
| 2188 | +{ |
| 2189 | + const gchar *save_as[] = { "File", "Save", "As...", NULL }; |
| 2190 | + const gchar *save[] = { "File", "Save", NULL }; |
| 2191 | + |
| 2192 | + g_assert_cmpint (calculate_distance ("save", (GStrv) save, NULL), |
| 2193 | + <, |
| 2194 | + calculate_distance ("save", (GStrv) save_as, NULL)); |
| 2195 | +} |
| 2196 | + |
| 2197 | /* Build the test suite */ |
| 2198 | static void |
| 2199 | test_distance_suite (void) |
| 2200 | @@ -196,6 +247,7 @@ |
| 2201 | g_test_add_func ("/hud/distance/duplicates", test_distance_dups); |
| 2202 | g_test_add_func ("/hud/distance/variety", test_distance_variety); |
| 2203 | g_test_add_func ("/hud/distance/french_pref", test_distance_french_pref); |
| 2204 | + g_test_add_func ("/hud/distance/extra_terms", test_distance_extra_terms); |
| 2205 | return; |
| 2206 | } |
| 2207 | |
| 2208 | |
| 2209 | === added file 'tests/word-list.h' |
| 2210 | --- tests/word-list.h 1970-01-01 00:00:00 +0000 |
| 2211 | +++ tests/word-list.h 2012-04-03 12:30:26 +0000 |
| 2212 | @@ -0,0 +1,364 @@ |
| 2213 | +const gchar *word_list[] = { |
| 2214 | + "sap", "sat", "sad", "rat", "rap", "ram", "rag", "nap", "Nat", "mat", |
| 2215 | + "map", "mad", "lap", "lag", "lad", "fat", "fan", "fad", "fin", "fit", |
| 2216 | + "lid", "lip", "lit", "mid", "mitt", "nit", "nip", "rid", "rig", "rim", |
| 2217 | + "rip", "Sid", "sin", "sip", "log", "mom", "mop", "nod", "rod", "Ron", |
| 2218 | + "rot", "sod", "fun", "mud", "mum", "nut", "rug", "rut", "sum", "sun", |
| 2219 | + "fed", "led", "leg", "met", "Ned", "net", "bag", "bad", "bam", "bat", |
| 2220 | + "cap", "cab", "dad", "Dan", "gas", "gag", "ham", "hat", "jab", "jam", |
| 2221 | + "pan", "pat", "tab", "tag", "tan", "tap", "bid", "dig", "dip", "hid", |
| 2222 | + "hit", "hip", "Jim", "jig", "kin", "kid", "pin", "pit", "pig", "tin", |
| 2223 | + "tip", "Tim", "cop", "con", "Don", "dog", "hop", "hog", "job", "jog", |
| 2224 | + "pot", "pop", "top", "Tom", "bug", "bud", "bum", "cup", "cub", "dud", |
| 2225 | + "dug", "Gus", "gun", "hum", "jug", "pup", "tub", "tug", "beg", "bed", |
| 2226 | + "bet", "hen", "jet", "Ken", "pen", "pet", "peg", "pep", "the", "of", |
| 2227 | + "and", "a", "to", "in", "is", "you", "that", "it", "he", "was", "for", |
| 2228 | + "on", "are", "as", "with", "his", "they", "I", "at", "be", "this", |
| 2229 | + "have", "from", "or", "one", "had", "by", "word", "but", "not", |
| 2230 | + "what", "all", "were", "we", "when", "your", "can", "said", "there", |
| 2231 | + "use", "an", "each", "which", "she", "do", "how", "their", "if", |
| 2232 | + "will", "up", "other", "about", "out", "many", "then", "them", |
| 2233 | + "these", "so", "some", "her", "would", "make", "like", "him", "into", |
| 2234 | + "time", "has", "look", "two", "more", "write", "go", "see", "number", |
| 2235 | + "no", "way", "could", "people", "my", "than", "first", "water", |
| 2236 | + "been", "call", "who", "oil", "its", "now", "find", "long", "down", |
| 2237 | + "day", "did", "get", "come", "made", "may", "part", "over", "new", |
| 2238 | + "sound", "take", "only", "little", "work", "know", "place", "year", |
| 2239 | + "live", "me", "back", "give", "most", "very", "after", "thing", "our", |
| 2240 | + "just", "name", "good", "sentence", "man", "think", "say", "great", |
| 2241 | + "where", "help", "through", "much", "before", "line", "right", "too", |
| 2242 | + "mean", "old", "any", "same", "tell", "boy", "follow", "came", "want", |
| 2243 | + "show", "also", "around", "farm", "three", "small", "set", "put", |
| 2244 | + "end", "does", "another", "well", "large", "must", "big", "even", |
| 2245 | + "such", "because", "turn", "here", "why", "ask", "went", "men", |
| 2246 | + "read", "need", "land", "different", "home", "us", "move", "try", |
| 2247 | + "kind", "hand", "picture", "again", "change", "off", "play", "spell", |
| 2248 | + "air", "away", "animal", "house", "point", "page", "letter", "mother", |
| 2249 | + "answer", "found", "study", "still", "learn", "should", "America", |
| 2250 | + "world", "high", "every", "near", "add", "food", "between", "own", |
| 2251 | + "below", "country", "plant", "last", "school", "father", "keep", |
| 2252 | + "tree", "never", "start", "city", "earth", "eye", "light", "thought", |
| 2253 | + "head", "under", "story", "saw", "left", "don't", "few", "while", |
| 2254 | + "along", "might", "chose", "something", "seem", "next", "hard", |
| 2255 | + "open", "example", "begin", "life", "always", "those", "both", |
| 2256 | + "paper", "together", "got", "group", "often", "run", "important", |
| 2257 | + "until", "children", "side", "feet", "car", "mile", "night", "walk", |
| 2258 | + "white", "sea", "began", "grow", "took", "river", "four", "carry", |
| 2259 | + "state", "once", "book", "hear", "stop", "without", "second", "late", |
| 2260 | + "miss", "idea", "enough", "eat", "face", "watch", "far", "Indian", |
| 2261 | + "really", "almost", "let", "above", "girl", "sometimes", "mountain", |
| 2262 | + "cut", "young", "talk", "soon", "list", "song", "being", "leave", |
| 2263 | + "family", "it's", "am", "ate", "best", "better", "black", "blue", |
| 2264 | + "bring", "brown", "buy", "clean", "cold", "done", "draw", "drink", |
| 2265 | + "eight", "fall", "fast", "five", "fly", "full", "funny", "gave", |
| 2266 | + "giving", "goes", "green", "hold", "hot", "hurt", "jump", "laugh", |
| 2267 | + "myself", "pick", "please", "pretty", "pull", "ran", "red", "ride", |
| 2268 | + "round", "seven", "shall", "sing", "sit", "six", "sleep", "ten", |
| 2269 | + "thank", "today", "upon", "warm", "wash", "wish", "yellow", "yes", |
| 2270 | + "act", "ant", "bake", "band", "bank", "bell", "belt", "Ben", "bend", |
| 2271 | + "bent", "Bess", "bike", "bit", "bite", "blast", "bled", "blend", |
| 2272 | + "blimp", "blink", "bliss", "block", "blond", "blot", "bluff", "blunt", |
| 2273 | + "bone", "brag", "brand", "brass", "brat", "bred", "bride", "brig", |
| 2274 | + "brim", "broke", "brunt", "brute", "bump", "bun", "bunt", "bust", |
| 2275 | + "camp", "cane", "can't", "cape", "cast", "cat", "clad", "clam", |
| 2276 | + "clamp", "clan", "clap", "clasp", "class", "cliff", "cling", "clink", |
| 2277 | + "clip", "close", "clot", "club", "clump", "clung", "cone", "crab", |
| 2278 | + "craft", "cram", "cramp", "crib", "crime", "crisp", "crop", "crust", |
| 2279 | + "cure", "cute", "dam", "damp", "den", "dent", "dim", "dime", "dine", |
| 2280 | + "dire", "dive", "dope", "draft", "drag", "drank", "dress", "drift", |
| 2281 | + "drill", "drip", "drop", "drove", "drug", "drum", "dump", "dust", |
| 2282 | + "eve", "fact", "fade", "fell", "felt", "file", "fill", "film", "fine", |
| 2283 | + "fire", "fist", "flag", "flap", "flat", "fled", "fling", "flip", |
| 2284 | + "flop", "flung", "flunk", "frame", "frank", "frill", "frisk", "frog", |
| 2285 | + "frost", "froze", "fume", "gasp", "gaze", "glad", "gland", "glass", |
| 2286 | + "glint", "globe", "glum", "golf", "grab", "grade", "gram", "gramp", |
| 2287 | + "grand", "grant", "grape", "grasp", "grass", "grill", "grim", "grin", |
| 2288 | + "grip", "gripe", "grump", "grunt", "gulp", "gust", "gut", "hate", |
| 2289 | + "held", "hide", "hint", "hire", "hole", "honk", "hope", "hose", "hug", |
| 2290 | + "hung", "hunt", "hut", "ink", "Jan", "Jane", "joke", "junk", "kept", |
| 2291 | + "kit", "kite", "lamp", "lick", "lift", "lime", "limp", "lock", "luck", |
| 2292 | + "lump", "Mack", "mask", "mass", "mast", "mate", "melt", "mend", |
| 2293 | + "Mick", "Mike", "milk", "mill", "mint", "mist", "mite", "mope", |
| 2294 | + "mule", "mute", "neck", "nest", "nine", "nose", "note", "pane", |
| 2295 | + "pant", "pass", "past", "pest", "Pete", "pike", "pile", "pill", |
| 2296 | + "pine", "plan", "plane", "plank", "plate", "plop", "plot", "plug", |
| 2297 | + "plum", "plump", "plus", "poke", "pole", "pomp", "pond", "pope", |
| 2298 | + "prank", "press", "pride", "prime", "print", "prize", "prop", "punk", |
| 2299 | + "pure", "raft", "ripe", "robe", "rock", "rode", "romp", "rope", |
| 2300 | + "rose", "Runs", "runt", "rust", "sack", "sake", "Sam", "sand", "sank", |
| 2301 | + "save", "scab", "scale", "scalp", "scan", "scare", "scat", "scope", |
| 2302 | + "scram", "scrap", "script", "self", "sell", "send", "sent", "sick", |
| 2303 | + "site", "size", "skate", "skid", "skill", "skim", "skin", "skip", |
| 2304 | + "skit", "skunk", "slam", "slang", "slant", "slap", "slat", "slate", |
| 2305 | + "slave", "sled", "slept", "slide", "slim", "sling", "slip", "slob", |
| 2306 | + "slope", "slot", "slug", "slum", "slump", "smack", "smell", "smile", |
| 2307 | + "smog", "smoke", "smug", "snack", "snag", "snake", "snap", "sniff", |
| 2308 | + "snip", "snipe", "snub", "snug", "sock", "soft", "span", "spank", |
| 2309 | + "spat", "sped", "spend", "spent", "spill", "spin", "spine", "spit", |
| 2310 | + "spite", "splat", "splint", "split", "spoke", "spot", "spun", "spunk", |
| 2311 | + "stab", "stack", "stale", "stamp", "stand", "stem", "step", "stiff", |
| 2312 | + "sting", "stink", "stomp", "stone", "stove", "strand", "strap", |
| 2313 | + "strip", "stripe", "struck", "strung", "stuck", "stump", "stun", |
| 2314 | + "suck", "sung", "swam", "swang", "swell", "swift", "swim", "swing", |
| 2315 | + "swung", "tack", "tam", "tame", "tape", "tent", "test", "tide", |
| 2316 | + "tile", "till", "tilt", "trade", "tramp", "trap", "trend", "trick", |
| 2317 | + "trim", "trip", "trot", "trunk", "trust", "twang", "twig", "twin", |
| 2318 | + "twist", "van", "vane", "wave", "weld", "wet", "win", "wind", "wine", |
| 2319 | + "wire", "yoke", "absent", "admit", "album", "ball", "bang", "banging", |
| 2320 | + "basket", "bathtub", "bedbug", "bench", "bib", "bill", "blank", |
| 2321 | + "blasted", "blended", "blush", "bobsled", "box", "branch", "brave", |
| 2322 | + "brush", "bunch", "bus", "camping", "care", "case", "catnip", "cave", |
| 2323 | + "chest", "chill", "chin", "chip", "chipmunk", "chop", "chunk", |
| 2324 | + "clock", "cloth", "contest", "crack", "crash", "crashing", "crept", |
| 2325 | + "cross", "crush", "cuff", "dash", "deck", "dentist", "dish", |
| 2326 | + "disrupt", "disrupted", "dot", "drinking", "dusted", "expanded", |
| 2327 | + "fang", "fib", "finishing", "fish", "fix", "flute", "fog", "fox", |
| 2328 | + "Fran", "fuss", "gift", "goblin", "gum", "hall", "hang", "Hank", |
| 2329 | + "himself", "hotrod", "huff", "hunted", "index", "insisted", |
| 2330 | + "insisting", "insulted", "invent", "invented", "Jack", "jumping", |
| 2331 | + "king", "kiss", "lane", "lapdog", "lasted", "lending", "loft", "lost", |
| 2332 | + "lug", "lunch", "lung", "mall", "mascot", "math", "mess", "mob", |
| 2333 | + "mug", "napkin", "pack", "Pam", "path", "picnic", "pigpen", "pinball", |
| 2334 | + "pinch", "planted", "plastic", "problem", "public", "publishing", |
| 2335 | + "puff", "punishing", "quake", "rake", "rash", "rented", "rest", |
| 2336 | + "rested", "rich", "ring", "ringing", "rub", "safe", "sale", "sang", |
| 2337 | + "sash", "shack", "shed", "shelf", "shell", "shifted", "shine", "ship", |
| 2338 | + "shop", "shrimp", "shrinking", "shrunk", "shut", "sink", "sinking", |
| 2339 | + "sits", "splash", "splashing", "squinted", "standing", "Steve", |
| 2340 | + "stub", "stuff", "stunt", "sub", "sunfish", "sunk", "sunlit", |
| 2341 | + "sunset", "suntan", "swishing", "talented", "tall", "tank", "throne", |
| 2342 | + "thud", "tick", "tilted", "tiptop", "toss", "trusted", "twisted", |
| 2343 | + "upset", "vent", "vest", "wag", "wall", "wax", "whiff", "whip", |
| 2344 | + "whiplash", "wig", "wing", "wink", "wipe", "wise", "yell", "zap", |
| 2345 | + "zigzag", "called", "circle", "friend", "Mr.", "Mrs.", "nothing", |
| 2346 | + "says", "Ann", "Beth", "Bing", "Blanch", "bong", "Buck", "burnt", |
| 2347 | + "Burt", "Carmen", "Cass", "caterpillar", "Chad", "cheer", "chomp", |
| 2348 | + "Chuck", "chuckle", "chug", "Clark", "click", "climb", "clown", |
| 2349 | + "cluck", "coal", "complain", "croak", "crunch", "Curtis", "curve", |
| 2350 | + "darts", "dinner", "diver", "doctor", "doll", "door", "Doris", |
| 2351 | + "Ellen", "fellow", "fiddle", "firm", "fishing", "Fitch", "foam", |
| 2352 | + "forever", "forgot", "forth", "front", "Gert", "gobble", "gone", |
| 2353 | + "gosh", "granddad", "grandpa", "grinned", "grumble", "hatch", "Herb", |
| 2354 | + "hill", "horse", "hush", "insect", "Jeff", "jiggle", "Jill", "Jill's", |
| 2355 | + "Josh", "jumble", "kick", "Kim", "Kirk", "larch", "library", "lived", |
| 2356 | + "lives", "market", "Mitch", "mix", "napper", "nibble", "Nick", "Norm", |
| 2357 | + "Oh", "onto", "owner", "patch", "peck", "perfect", "ping", "Pip", |
| 2358 | + "pong", "quick", "quill", "quilt", "Quinn", "quit", "reward", "Rex", |
| 2359 | + "Rick", "Rivera", "roam", "ruff", "Sanchez", "served", "Seth", |
| 2360 | + "sister", "Sloan", "smash", "snort", "snuggle", "soup", "sparkle", |
| 2361 | + "sprinkle", "squirt", "stick", "sudden", "sunburn", "surprise", |
| 2362 | + "swimmer", "Tad", "tadpole", "Ted", "Tess", "Texas", "tickle", "toad", |
| 2363 | + "Todd", "turf", "twinkle", "twitch", "umbrella", "uncle", "wham", |
| 2364 | + "whirl", "whisper", "whistle", "wiggle", "window", "Winkle", |
| 2365 | + "writing", "yet", "York", "zing", "zip", "able", "ace", "added", |
| 2366 | + "afraid", "afternoon", "age", "ahead", "annoy", "anything", "anyway", |
| 2367 | + "anywhere", "ape", "applaud", "arm", "artist", "attack", "attic", |
| 2368 | + "auto", "avoid", "awesome", "awful", "awning", "babble", "baby", |
| 2369 | + "baffle", "bald", "ballplayer", "bark", "base", "baseball", "basin", |
| 2370 | + "basketball", "batch", "bath", "battle", "beach", "bead", "beam", |
| 2371 | + "bean", "beanstalk", "beast", "beat", "bedtime", "bee", "beef", |
| 2372 | + "beehive", "beet", "begging", "behind", "bird", "birthday", "bleach", |
| 2373 | + "blew", "blind", "bloom", "blow", "blown", "bluebird", "blueprint", |
| 2374 | + "blur", "boast", "boat", "bob", "body", "boil", "bold", "bonnet", |
| 2375 | + "bonus", "boost", "boot", "born", "bottle", "bowl", "brace", "braid", |
| 2376 | + "brain", "brainstorm", "brake", "brawl", "bread", "breakfast", |
| 2377 | + "breath", "brick", "bright", "broil", "broiler", "broom", "bruise", |
| 2378 | + "bubble", "buddy", "built", "bundle", "bunk", "bunny", "burn", |
| 2379 | + "burst", "buses", "butterfly", "button", "buzz", "cabin", "cage", |
| 2380 | + "cake", "camel", "candle", "candy", "careful", "cart", "catch", |
| 2381 | + "cattle", "cell", "cent", "chain", "chalk", "champ", "charge", |
| 2382 | + "chart", "chase", "chat", "check", "cheerful", "cheese", "chess", |
| 2383 | + "chew", "chick", "child", "chime", "chirp", "choice", "chore", |
| 2384 | + "church", "churn", "claim", "classmate", "clay", "clerk", "clever", |
| 2385 | + "clue", "clutch", "coach", "coat", "coax", "coil", "coin", "collect", |
| 2386 | + "colorful", "cool", "core", "cork", "corn", "cowboy", "cozy", "crate", |
| 2387 | + "crawl", "cream", "crew", "crinkle", "crow", "cruise", "cry", |
| 2388 | + "cuddle", "cupcake", "curb", "curl", "dab", "daddy", "dangle", |
| 2389 | + "Danny", "dark", "dart", "date", "dawn", "daylight", "dead", "deaf", |
| 2390 | + "dealt", "decent", "deep", "delight", "desk", "die", "dimple", "dirt", |
| 2391 | + "ditch", "doghouse", "double", "dragon", "dragonfly", "drain", |
| 2392 | + "dread", "dream", "drew", "driveway", "droop", "dry", "duck", "due", |
| 2393 | + "dunk", "dusk", "easel", "easy", "egg", "elbow", "enjoy", "ever", |
| 2394 | + "evergreen", "everyone", "everything", "everywhere", "explore", |
| 2395 | + "fabric", "fail", "faithful", "fame", "fault", "fawn", "feast", |
| 2396 | + "feed", "feel", "fence", "fern", "fetch", "fifty", "fight", "finish", |
| 2397 | + "firefighter", "flagpole", "flash", "flashlight", "flaunt", "flaw", |
| 2398 | + "flight", "float", "flow", "fluffy", "foal", "foil", "fold", "fool", |
| 2399 | + "forest", "forget", "fork", "form", "fort", "fraud", "fray", "free", |
| 2400 | + "fresh", "Friday", "fried", "fright", "frozen", "fruit", "fry", "fur", |
| 2401 | + "game", "garden", "gate", "gel", "gem", "germ", "giggle", "ginger", |
| 2402 | + "giraffe", "gleam", "glean", "glow", "gluestick", "goat", "going", |
| 2403 | + "gold", "goose", "Grace", "graceful", "grain", "grapefruit", |
| 2404 | + "grasshopper", "grateful", "gray", "grew", "groan", "grown", |
| 2405 | + "grown-up", "growth", "gumball", "habit", "hail", "handwriting", |
| 2406 | + "happen", "happy", "harm", "harmful", "harp", "haul", "haunt", "hawk", |
| 2407 | + "hay", "health", "heat", "heavy", "heel", "hello", "helpful", |
| 2408 | + "herself", "hi", "hidden", "highway", "hoist", "homemade", "homework", |
| 2409 | + "hoot", "hopeful", "hopscotch", "horn", "host", "houseboat", |
| 2410 | + "housefly", "huddle", "human", "humid", "hummingbird", "hump", |
| 2411 | + "hungry", "hunk", "hurdle", "hurl", "ice", "inch", "inside", |
| 2412 | + "instead", "itch", "Jake", "jar", "jaw", "Jean", "jeep", "jelly", |
| 2413 | + "jellyfish", "Jen", "jerk", "Jimmy", "jingle", "Jo", "Joan", "join", |
| 2414 | + "joint", "joy", "juggle", "juice", "jungle", "kickball", "kingfish", |
| 2415 | + "kits", "kitten", "kitty", "knack", "knee", "kneecap", "kneel", |
| 2416 | + "knelt", "knew", "knife", "knight", "knit", "knob", "knock", |
| 2417 | + "knockout", "knot", "knothole", "known", "lace", "lack", "lake", |
| 2418 | + "latch", "launch", "law", "lawn", "lay", "lazy", "leap", "leather", |
| 2419 | + "Lee", "lemon", "less", "lie", "lighthouse", "limit", "link", |
| 2420 | + "lipstick", "living", "load", "loaf", "loop", "loose", "lot", "low", |
| 2421 | + "lunchbox", "Mabel", "mail", "main", "mane", "march", "mark", "match", |
| 2422 | + "Matt", "meant", "meat", "meet", "mice", "middle", "mild", "mind", |
| 2423 | + "mine", "mitten", "moan", "model", "moist", "mold", "moment", |
| 2424 | + "Monday", "moo", "mood", "motel", "mow", "munch", "music", "muzzle", |
| 2425 | + "nab", "nail", "nanny", "Nate", "neat", "necktie", "nerve", |
| 2426 | + "newscast", "newspaper", "nice", "nickname", "nighttime", "nips", |
| 2427 | + "noise", "noon", "north", "nowhere", "oak", "oat", "oatmeal", |
| 2428 | + "object", "oink", "ooze", "outside", "ox", "pace", "paddle", "pail", |
| 2429 | + "pain", "painful", "paint", "pal", "pancake", "paperback", "park", |
| 2430 | + "pause", "paw", "pawn", "pay", "peace", "peaceful", "peach", "peak", |
| 2431 | + "peddle", "peep", "pencil", "pennies", "penny", "perch", "piano", |
| 2432 | + "pie", "pillow", "pilot", "pink", "pipe", "pitch", "ploy", "poison", |
| 2433 | + "pony", "pooch", "poodle", "pool", "popcorn", "porch", "port", "post", |
| 2434 | + "poster", "potpie", "pretzel", "price", "proof", "pupil", "puppy", |
| 2435 | + "purple", "purse", "puzzle", "quack", "queen", "quicksand", "rabbit", |
| 2436 | + "race", "rack", "rage", "rail", "rain", "rainbow", "ranch", "rang", |
| 2437 | + "rattle", "raw", "ray", "reach", "ready", "recycle", "refund", |
| 2438 | + "renew", "restful", "return", "ribbon", "rice", "riddle", "rind", |
| 2439 | + "rink", "rise", "road", "roast", "rob", "robin", "robot", "room", |
| 2440 | + "roost", "row", "Roy", "royal", "ruled", "rumble", "runway", "rush", |
| 2441 | + "saddle", "sag", "sail", "sailboat", "salad", "sample", "sandbox", |
| 2442 | + "sandpaper", "sandy", "Saturday", "sauce", "saucer", "scarf", "scold", |
| 2443 | + "scorch", "score", "scrape", "scratch", "scream", "screen", "screw", |
| 2444 | + "scrub", "seal", "seat", "serve", "settle", "shade", "shadow", |
| 2445 | + "shake", "shaking", "shameful", "shape", "share", "shark", "sharp", |
| 2446 | + "sheep", "shipwreck", "shirt", "shore", "short", "shrink", "shrub", |
| 2447 | + "shrug", "shy", "sidewalk", "sigh", "sight", "silence", "silly", |
| 2448 | + "simple", "sips", "sir", "siren", "sky", "slice", "slick", "slight", |
| 2449 | + "slow", "slur", "sly", "smart", "smiling", "smooth", "snail", "sneak", |
| 2450 | + "snooze", "snore", "snow", "snowball", "snowflake", "snowman", "soak", |
| 2451 | + "soap", "sofa", "soil", "someone", "somewhere", "sore", "sort", "soy", |
| 2452 | + "space", "spark", "speak", "splendid", "splotch", "spoil", "spool", |
| 2453 | + "spoon", "sport", "sprain", "sprawl", "spray", "spread", "spring", |
| 2454 | + "sprint", "spruce", "spur", "squawk", "stage", "star", "startle", |
| 2455 | + "stay", "steam", "steep", "stern", "stiffer", "stir", "stool", |
| 2456 | + "store", "stork", "storm", "strain", "strange", "straw", "stray", |
| 2457 | + "stream", "street", "stretch", "strict", "strike", "string", "strong", |
| 2458 | + "stung", "sue", "suit", "suitcase", "sunblock", "Sunday", "sunflower", |
| 2459 | + "sunny", "sunrise", "sunshine", "surf", "sway", "sweat", "sweep", |
| 2460 | + "sweet", "swerve", "swimming", "swirl", "switch", "tablet", "tail", |
| 2461 | + "taps", "tattle", "taught", "tea", "teach", "teacher", "team", |
| 2462 | + "tease", "teeth", "tennis", "thankful", "thick", "thigh", "thimble", |
| 2463 | + "thin", "thinner", "thinnest", "third", "thirst", "thorn", "thread", |
| 2464 | + "threat", "thrill", "throat", "throw", "Thursday", "tie", "tiger", |
| 2465 | + "tight", "tired", "toast", "toil", "told", "tool", "tooth", "topcoat", |
| 2466 | + "torch", "torn", "tot", "tow", "toy", "trace", "traffic", "train", |
| 2467 | + "trash", "travel", "tray", "tread", "treetop", "trouble", "truck", |
| 2468 | + "trumpet", "Tuesday", "tugboat", "tulip", "tumble", "tummy", "turtle", |
| 2469 | + "tusk", "twirl", "ugly", "unzip", "useful", "value", "vanish", "verb", |
| 2470 | + "verse", "vine", "visit", "visitor", "voice", "void", "vote", |
| 2471 | + "waddle", "wage", "wagon", "wait", "wake", "waves", "wealth", |
| 2472 | + "weather", "weave", "web", "Wednesday", "weed", "week", "weekend", |
| 2473 | + "wettest", "whack", "whale", "whatever", "wheat", "wheel", "whenever", |
| 2474 | + "whisk", "whiz", "wide", "wife", "wild", "windmill", "wonderful", |
| 2475 | + "wore", "worn", "wrap", "wreath", "wreck", "wrench", "wriggle", |
| 2476 | + "wrinkle", "wrist", "wristwatch", "written", "wrong", "wrote", "yak", |
| 2477 | + "yard", "yardstick", "yarn", "yawn", "yum", "Zack", "zebra", "zoo", |
| 2478 | + "zoom", "ban", "bar", "winter", "bay", "wear", "bye", "mitten", "aid", |
| 2479 | + "aim", "arc", "art", "ash", "axe", "cog", "cot", "cow", "dew", "ear", |
| 2480 | + "eel", "elf", "elk", "elm", "era", "fee", "fig", "fir", "flu", "gap", |
| 2481 | + "guy", "icy", "inn", "ivy", "Jay", "key", "nag", "oar", "odd", "owe", |
| 2482 | + "owl", "pea", "pod", "rib", "sew", "ski", "sob", "son", "sow", "spa", |
| 2483 | + "spy", "tar", "tax", "toe", "vet", "war", "won", "yap", "zen", "ax", |
| 2484 | + "bait", "barn", "bawl", "beak", "birth", "blab", "blade", "blame", |
| 2485 | + "blaze", "bleed", "bless", "blip", "blurb", "blurt", "bog", "bond", |
| 2486 | + "boo", "books", "booth", "bow", "boys", "brad", "bran", "brawn", |
| 2487 | + "brisk", "brook", "broth", "brother", "card", "cash", "cask", "caw", |
| 2488 | + "cheap", "cheek", "chow", "clack", "clang", "clash", "claw", "cloak", |
| 2489 | + "clod", "coast", "cob", "cod", "code", "cook", "coop", "cord", "cost", |
| 2490 | + "count", "coy", "crane", "crick", "crock", "crook", "crown", "crude", |
| 2491 | + "crumb", "cube", "cue", "dear", "disk", "dock", "doze", "drake", |
| 2492 | + "drape", "drawn", "drive", "dude", "east", "Ed", "faint", "flake", |
| 2493 | + "flame", "flesh", "flick", "flirt", "flit", "flock", "floor", "flour", |
| 2494 | + "flown", "flush", "fond", "foot", "fowl", "Fred", "fret", "frizz", |
| 2495 | + "frying", "fuse", "gang", "glade", "glaze", "glee", "glen", "glide", |
| 2496 | + "glob", "gloss", "goal", "gown", "grate", "greet", "grit", "grouch", |
| 2497 | + "growl", "gruff", "heal", "heap", "hem", "hike", "hood", "hoof", |
| 2498 | + "hook", "hoop", "howl", "huge", "husk", "imp", "intern", "jail", |
| 2499 | + "jib", "June", "jut", "laid", "lame", "leak", "lend", "lent", "lien", |
| 2500 | + "lint", "loan", "loin", "lone", "loot", "lop", "lord", "loud", "lurk", |
| 2501 | + "lute", "maid", "maze", "meal", "mirth", "mock", "mole", "moon", |
| 2502 | + "mouse", "mouth", "murk", "musk", "nook", "nurse", "pad", "paid", |
| 2503 | + "peek", "peel", "pelt", "perk", "plain", "plants", "player", "plod", |
| 2504 | + "plow", "plums", "pork", "prance", "pray", "prayer", "present", |
| 2505 | + "prim", "prince", "probe", "prod", "program", "proud", "prowl", "pry", |
| 2506 | + "punt", "raid", "rant", "real", "rent", "risk", "Rome", "roof", |
| 2507 | + "rude", "rung", "scald", "scar", "scoop", "scoot", "scout", "scowl", |
| 2508 | + "scuff", "scum", "seek", "seen", "shawl", "sheet", "shirk", "shook", |
| 2509 | + "shoot", "shot", "shout", "shown", "sift", "silk", "sixth", "skips", |
| 2510 | + "skirt", "skull", "slash", "slaw", "slay", "slid", "slit", "slop", |
| 2511 | + "smear", "smith", "smock", "smoky", "snare", "snatch", "sneeze", |
| 2512 | + "snob", "snoop", "snuff", "soot", "sour", "south", "speck", "speech", |
| 2513 | + "speed", "spike", "spry", "spud", "spurn", "spurt", "stain", "stall", |
| 2514 | + "Stan", "steal", "stood", "sulk", "swamp", "swan", "swap", "swarm", |
| 2515 | + "swat", "swatch", "swept", "swig", "swine", "swish", "swoop", "sworn", |
| 2516 | + "swum", "tale", "task", "tend", "tenth", "term", "thumb", "tint", |
| 2517 | + "tock", "town", "toys", "track", "trail", "tram", "trek", "tribe", |
| 2518 | + "trod", "Troy", "tune", "tweed", "tweet", "twelve", "twenty", "twice", |
| 2519 | + "twine", "twitter", "urge", "urn", "uses", "vase", "vow", "wade", |
| 2520 | + "wed", "wept", "west", "whine", "whir", "wick", "wilt", "woke", |
| 2521 | + "wood", "woof", "wool", "wove", "wow", "zest", "zone", "against", |
| 2522 | + "American", "among", "asked", "course", "days", "didn't", "during", |
| 2523 | + "eyes", "general", "given", "however", "later", "order", "possible", |
| 2524 | + "president", "rather", "since", "states", "system", "things", |
| 2525 | + "though", "toward", "united", "used", "years", "color", "suddenly", |
| 2526 | + "zipper", "ah", "ho", "Ma", "Pa", "TV", "ago", "baa", "God", "hoe", |
| 2527 | + "I'd", "I'm", "sis", "ill", "O.K", "ache", "aunt", "bare", "bear", |
| 2528 | + "boom", "busy", "dare", "lose", "love", "none", "rule", "sure", |
| 2529 | + "tire", "tube", "copy", "edge", "else", "fizz", "glue", "hair", |
| 2530 | + "half", "hour", "I'll", "I've", "July", "kill", "lady", "lamb", |
| 2531 | + "leaf", "liar", "lion", "mama", "mash", "meow", "mush", "obey", |
| 2532 | + "okay", "ouch", "oven", "papa", "pour", "push", "roll", "shoe", |
| 2533 | + "soda", "tiny", "worm", "TRUE", "U.S.", "agree", "alive", "apple", |
| 2534 | + "April", "awake", "blood", "break", "chair", "cloud", "cough", |
| 2535 | + "cover", "dance", "erase", "phone", "piece", "whole", "whose", |
| 2536 | + "adult", "angry", "belly", "death", "eagle", "empty", "extra", |
| 2537 | + "hurry", "maybe", "money", "movie", "nasty", "party", "pizza", |
| 2538 | + "quiet", "sorry", "stair", "sugar", "table", "taste", "threw", |
| 2539 | + "touch", "towel", "truth", "waist", "waste", "woman", "women", |
| 2540 | + "won't", "worse", "anyone", "arrive", "asleep", "August", "avenue", |
| 2541 | + "behave", "bridge", "carpet", "cereal", "choose", "cookie", "corner", |
| 2542 | + "crayon", "danger", "minute", "banana", "bucket", "carrot", "dollar", |
| 2543 | + "finger", "flower", "gentle", "handle", "listen", "mirror", "monkey", |
| 2544 | + "nickel", "nobody", "orange", "parent", "person", "pocket", "potato", |
| 2545 | + "puppet", "rocket", "search", "secret", "seesaw", "shovel", "shower", |
| 2546 | + "silent", "spider", "sponge", "squash", "squeak", "sticky", "summer", |
| 2547 | + "supper", "they'd", "ticket", "tiptoe", "tissue", "tomato", "tongue", |
| 2548 | + "turkey", "unfair", "wonder", "U.S.A.", "airport", "anybody", |
| 2549 | + "balloon", "bedroom", "bicycle", "brownie", "cartoon", "ceiling", |
| 2550 | + "channel", "chicken", "garbage", "promise", "squeeze", "address", |
| 2551 | + "blanket", "earache", "excited", "good-bye", "grandma", "grocery", |
| 2552 | + "indoors", "January", "kitchen", "lullaby", "monster", "morning", |
| 2553 | + "naughty", "October", "pajamas", "pretend", "quarter", "shampoo", |
| 2554 | + "stomach", "there's", "they'll", "they're", "they've", "tonight", |
| 2555 | + "unhappy", "airplane", "alphabet", "bathroom", "favorite", "medicine", |
| 2556 | + "December", "dinosaur", "elephant", "February", "football", |
| 2557 | + "forehead", "headache", "hospital", "lollipop", "November", |
| 2558 | + "outdoors", "question", "railroad", "remember", "sandwich", |
| 2559 | + "scissors", "shoulder", "softball", "tomorrow", "upstairs", |
| 2560 | + "vacation", "restroom", "astronaut", "beautiful", "bumblebee", |
| 2561 | + "cardboard", "chocolate", "Christmas", "classroom", "cranberry", |
| 2562 | + "drugstore", "furniture", "milkshake", "nightmare", "telephone", |
| 2563 | + "difficult", "everybody", "hamburger", "September", "spaceship", |
| 2564 | + "spaghetti", "stoplight", "underwear", "yesterday", "automobile", |
| 2565 | + "blackboard", "downstairs", "photograph", "strawberry", "television", |
| 2566 | + "toothbrush", "toothpaste", "baby-sitter", "grandfather", |
| 2567 | + "grandmother", "kindergarten", "refrigerator", "Thanksgiving", |
| 2568 | + "hide-and-seek", "pumpkin", "salt", "melted", "handed", "printed", |
| 2569 | + "landed", "wanted", "filled", "showed", "hugged", "tugged", "planned", |
| 2570 | + "jogged", "spilled", "smelled", "grilled", "slammed", "rushed", |
| 2571 | + "spelled", "saved", "baked", "named", "lined", "smiled", "closed", |
| 2572 | + "helped", "jumped", "looked", "clapped", "tapped", "kicked", |
| 2573 | + "dropped", "zipped", "wished", "pitched", "missed", "walked", |
| 2574 | + "worked", "she'll", "you'll", "he'll", "we're", "she's", "he's", |
| 2575 | + "you're", "we're" |
| 2576 | +}; |

A couple of non-critical comments:
* Next time please use move for things like the schemas as then diffs are tracked on them which makes reviewing them much easier.
* I think the "magic 32" should probably be a #define just so that if someone changes it it will be changed everywhere. For instance, changing it in _new() could cause an assert failure further down the road.
* A comment on hud_token_ list_distance_ slow () on why the assert isn't insane would be good :-)
* Love the test :-)