Commit | Line | Data |
---|---|---|
24008bc4 MA |
1 | ;;; xesam.el --- Xesam interface to search engines. |
2 | ||
ba318903 | 3 | ;; Copyright (C) 2008-2014 Free Software Foundation, Inc. |
24008bc4 MA |
4 | |
5 | ;; Author: Michael Albinus <michael.albinus@gmx.de> | |
6 | ;; Keywords: tools, hypermedia | |
ec076379 | 7 | ;; Obsolete-since: 24.4 |
24008bc4 MA |
8 | |
9 | ;; This file is part of GNU Emacs. | |
10 | ||
5d4cc458 | 11 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
24008bc4 | 12 | ;; it under the terms of the GNU General Public License as published by |
5d4cc458 GM |
13 | ;; the Free Software Foundation, either version 3 of the License, or |
14 | ;; (at your option) any later version. | |
24008bc4 MA |
15 | |
16 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | ;; GNU General Public License for more details. | |
20 | ||
21 | ;; You should have received a copy of the GNU General Public License | |
5d4cc458 | 22 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
24008bc4 MA |
23 | |
24 | ;;; Commentary: | |
25 | ||
ec076379 MA |
26 | ;; This file is obsolete. |
27 | ||
3cf235eb | 28 | ;; This package provides an interface to Xesam, a D-Bus based "eXtEnsible |
24008bc4 MA |
29 | ;; Search And Metadata specification". It has been tested with |
30 | ;; | |
31 | ;; xesam-glib 0.3.4, xesam-tools 0.6.1 | |
32 | ;; beagle 0.3.7, beagle-xesam 0.2 | |
5dd33078 | 33 | ;; strigi 0.5.11 |
24008bc4 MA |
34 | |
35 | ;; The precondition for this package is a D-Bus aware Emacs. This is | |
36 | ;; configured per default, when Emacs is built on a machine running | |
37 | ;; D-Bus. Furthermore, there must be at least one search engine | |
3cf235eb | 38 | ;; running, which supports the Xesam interface. Beagle and strigi have |
24008bc4 MA |
39 | ;; been tested; tracker, pinot and recoll are also said to support |
40 | ;; Xesam. You can check the existence of such a search engine by | |
41 | ;; | |
42 | ;; (dbus-list-queued-owners :session "org.freedesktop.xesam.searcher") | |
43 | ||
44 | ;; In order to start a search, you must load xesam.el: | |
45 | ;; | |
46 | ;; (require 'xesam) | |
47 | ||
48 | ;; xesam.el supports two types of queries, which are explained *very* short: | |
49 | ;; | |
50 | ;; * Full text queries. Just search keys shall be given, like | |
51 | ;; | |
52 | ;; hello world | |
53 | ;; | |
54 | ;; A full text query in xesam.el is restricted to files. | |
55 | ;; | |
56 | ;; * Xesam End User Search Language queries. The Xesam query language | |
57 | ;; is described at <http://xesam.org/main/XesamUserSearchLanguage>, | |
58 | ;; which must be consulted for the whole features. | |
59 | ;; | |
60 | ;; A query string consists of search keys, collectors, selectors, | |
61 | ;; and phrases. Search keys are words like in a full text query: | |
62 | ;; | |
63 | ;; hello word | |
64 | ;; | |
65 | ;; A selector is a tuple <keyword><relation>. <keyword> can be any | |
66 | ;; predefined Xesam keyword, the most common keywords are "ext" | |
67 | ;; (file name extension), "format " (mime type), "tag" (user | |
68 | ;; keywords) and "type" (types of items, like "audio", "file", | |
69 | ;; "picture", "attachment"). <relation> is a comparison to a value, | |
70 | ;; which must be a string (relation ":" or "=") or number (relation | |
71 | ;; "<=", ">=", "<", ">"): | |
72 | ;; | |
73 | ;; type:attachment ext=el | |
74 | ;; | |
75 | ;; A collector is one of the items "AND", "and", "&&", "OR", "or", | |
76 | ;; "||", or "-". The default collector on multiple terms is "AND"; | |
77 | ;; "-" means "AND NOT". | |
78 | ;; | |
79 | ;; albinus -type:file | |
80 | ;; | |
81 | ;; A phrase is a string enclosed in quotes, with appended modifiers | |
82 | ;; (single letters). Examples of modifiers are "c" (case | |
83 | ;; sensitive), "C" (case insensitive), "e" (exact match), "r" | |
84 | ;; (regular expression): | |
85 | ;; | |
86 | ;; "Hello world"c | |
87 | ||
88 | ;; You can customize, whether you want to apply a Xesam user query, or | |
89 | ;; a full text query. Note, that not every search engine supports | |
90 | ;; both query types. | |
91 | ;; | |
92 | ;; (setq xesam-query-type 'fulltext-query) | |
93 | ;; | |
e1dbe924 | 94 | ;; Another option to be customized is the number of hits to be |
24008bc4 MA |
95 | ;; presented at once. |
96 | ;; | |
97 | ;; (setq xesam-hits-per-page 50) | |
98 | ||
99 | ;; A search can be started by the command | |
100 | ;; | |
101 | ;; M-x xesam-search | |
102 | ;; | |
103 | ;; When several search engines are registered, the engine to be used | |
104 | ;; can be selected via minibuffer completion. Afterwards, the query | |
105 | ;; shall be entered in the minibuffer. | |
106 | ||
17668903 MA |
107 | ;; Search results are presented in a new buffer. This buffer has the |
108 | ;; major mode `xesam-mode', with the following keybindings: | |
109 | ||
110 | ;; SPC `scroll-up' | |
111 | ;; DEL `scroll-down' | |
112 | ;; < `beginning-of-buffer' | |
113 | ;; > `end-of-buffer' | |
114 | ;; q `quit-window' | |
115 | ;; z `kill-this-buffer' | |
116 | ;; g `revert-buffer' | |
117 | ||
118 | ;; The search results are represented by widgets. Navigation commands | |
119 | ;; are the usual widget navigation commands: | |
120 | ||
121 | ;; TAB `widget-forward' | |
122 | ;; <backtab> `widget-backward' | |
123 | ||
124 | ;; Applying RET, <down-mouse-1>, or <down-mouse-2> on a URL belonging | |
125 | ;; to the widget, brings up more details of the search hit. The way, | |
126 | ;; how this hit is presented, depends on the type of the hit. HTML | |
127 | ;; files are opened via `browse-url'. Local files are opened in a new | |
128 | ;; buffer, with highlighted search hits (highlighting can be toggled | |
129 | ;; by `xesam-minor-mode' in that buffer). | |
130 | ||
24008bc4 MA |
131 | ;;; Code: |
132 | ||
24008bc4 MA |
133 | (require 'dbus) |
134 | ||
24008bc4 MA |
135 | ;; Widgets are used to highlight the search results. |
136 | (require 'widget) | |
4edefb46 | 137 | (require 'wid-edit) |
24008bc4 MA |
138 | |
139 | ;; `run-at-time' is used in the signal handler. | |
140 | (require 'timer) | |
141 | ||
142 | ;; The default search field is "xesam:url". It must be inspected. | |
143 | (require 'url) | |
144 | ||
145 | (defgroup xesam nil | |
146 | "Xesam compatible interface to search engines." | |
147 | :group 'extensions | |
26f4b8ab | 148 | :group 'comm |
24008bc4 MA |
149 | :version "23.1") |
150 | ||
151 | (defcustom xesam-query-type 'user-query | |
152 | "Xesam query language type." | |
153 | :group 'xesam | |
154 | :type '(choice | |
155 | (const :tag "Xesam user query" user-query) | |
156 | (const :tag "Xesam fulltext query" fulltext-query))) | |
157 | ||
158 | (defcustom xesam-hits-per-page 20 | |
4edefb46 | 159 | "Number of search hits to be displayed in the result buffer." |
24008bc4 MA |
160 | :group 'xesam |
161 | :type 'integer) | |
162 | ||
4edefb46 MA |
163 | (defface xesam-mode-line '((t :inherit mode-line-emphasis)) |
164 | "Face to highlight mode line." | |
165 | :group 'xesam) | |
166 | ||
167 | (defface xesam-highlight '((t :inherit match)) | |
168 | "Face to highlight query entries. | |
333f9019 | 169 | It will be overlaid by `widget-documentation-face', so it shall |
4edefb46 MA |
170 | be different at least in one face property not set in that face." |
171 | :group 'xesam) | |
172 | ||
24008bc4 | 173 | (defvar xesam-debug nil |
de8bb89e | 174 | "Insert debug information in the help echo.") |
24008bc4 MA |
175 | |
176 | (defconst xesam-service-search "org.freedesktop.xesam.searcher" | |
177 | "The D-Bus name used to talk to Xesam.") | |
178 | ||
179 | (defconst xesam-path-search "/org/freedesktop/xesam/searcher/main" | |
180 | "The D-Bus object path used to talk to Xesam.") | |
181 | ||
182 | ;; Methods: "NewSession", "SetProperty", "GetProperty", | |
183 | ;; "CloseSession", "NewSearch", "StartSearch", "GetHitCount", | |
184 | ;; "GetHits", "GetHitData", "CloseSearch" and "GetState". | |
185 | ;; Signals: "HitsAdded", "HitsRemoved", "HitsModified", "SearchDone" | |
186 | ;; and "StateChanged". | |
187 | (defconst xesam-interface-search "org.freedesktop.xesam.Search" | |
188 | "The D-Bus Xesam search interface.") | |
189 | ||
190 | (defconst xesam-all-fields | |
818f12ce MA |
191 | '("xesam:35mmEquivalent" "xesam:aimContactMedium" "xesam:aperture" |
192 | "xesam:aspectRatio" "xesam:attachmentEncoding" "xesam:attendee" | |
ee7683eb | 193 | "xesam:audioBitrate" "xesam:audioChannels" "xesam:audioCodec" |
818f12ce MA |
194 | "xesam:audioCodecType" "xesam:audioSampleFormat" "xesam:audioSampleRate" |
195 | "xesam:author" "xesam:bcc" "xesam:birthDate" "xesam:blogContactURL" | |
24008bc4 MA |
196 | "xesam:cameraManufacturer" "xesam:cameraModel" "xesam:cc" "xesam:ccdWidth" |
197 | "xesam:cellPhoneNumber" "xesam:characterCount" "xesam:charset" | |
198 | "xesam:colorCount" "xesam:colorSpace" "xesam:columnCount" "xesam:comment" | |
199 | "xesam:commentCharacterCount" "xesam:conflicts" "xesam:contactMedium" | |
200 | "xesam:contactName" "xesam:contactNick" "xesam:contactPhoto" | |
d5081c1e | 201 | "xesam:contactURL" "xesam:contains" "xesam:contentKeyword" |
24008bc4 MA |
202 | "xesam:contentComment" "xesam:contentCreated" "xesam:contentModified" |
203 | "xesam:contentType" "xesam:contributor" "xesam:copyright" "xesam:creator" | |
204 | "xesam:definesClass" "xesam:definesFunction" "xesam:definesGlobalVariable" | |
205 | "xesam:deletionTime" "xesam:depends" "xesam:description" "xesam:device" | |
206 | "xesam:disclaimer" "xesam:documentCategory" "xesam:duration" | |
207 | "xesam:emailAddress" "xesam:eventEnd" "xesam:eventLocation" | |
208 | "xesam:eventStart" "xesam:exposureBias" "xesam:exposureProgram" | |
209 | "xesam:exposureTime" "xesam:faxPhoneNumber" "xesam:fileExtension" | |
210 | "xesam:fileSystemType" "xesam:flashUsed" "xesam:focalLength" | |
211 | "xesam:focusDistance" "xesam:formatSubtype" "xesam:frameCount" | |
212 | "xesam:frameRate" "xesam:freeSpace" "xesam:gender" "xesam:generator" | |
213 | "xesam:generatorOptions" "xesam:group" "xesam:hash" "xesam:hash" | |
214 | "xesam:height" "xesam:homeEmailAddress" "xesam:homePhoneNumber" | |
215 | "xesam:homePostalAddress" "xesam:homepageContactURL" | |
216 | "xesam:horizontalResolution" "xesam:icqContactMedium" "xesam:id" | |
217 | "xesam:imContactMedium" "xesam:interests" "xesam:interlaceMode" | |
218 | "xesam:isEncrypted" "xesam:isImportant" "xesam:isInProgress" | |
219 | "xesam:isPasswordProtected" "xesam:isRead" "xesam:isoEquivalent" | |
220 | "xesam:jabberContactMedium" "xesam:keyword" "xesam:language" "xesam:legal" | |
221 | "xesam:license" "xesam:licenseType" "xesam:lineCount" "xesam:links" | |
222 | "xesam:mailingPostalAddress" "xesam:maintainer" "xesam:md5Hash" | |
223 | "xesam:mediaCodec" "xesam:mediaCodecBitrate" "xesam:mediaCodecType" | |
224 | "xesam:meteringMode" "xesam:mimeType" "xesam:mountPoint" | |
225 | "xesam:msnContactMedium" "xesam:name" "xesam:occupiedSpace" | |
226 | "xesam:orientation" "xesam:originalLocation" "xesam:owner" | |
227 | "xesam:pageCount" "xesam:permissions" "xesam:phoneNumber" | |
228 | "xesam:physicalAddress" "xesam:pixelFormat" "xesam:primaryRecipient" | |
229 | "xesam:programmingLanguage" "xesam:rating" "xesam:receptionTime" | |
230 | "xesam:recipient" "xesam:related" "xesam:remoteUser" "xesam:rowCount" | |
231 | "xesam:sampleBitDepth" "xesam:sampleFormat" "xesam:secondaryRecipient" | |
232 | "xesam:sha1Hash" "xesam:size" "xesam:skypeContactMedium" | |
233 | "xesam:sourceCreated" "xesam:sourceModified" "xesam:storageSize" | |
234 | "xesam:subject" "xesam:supercedes" "xesam:title" "xesam:to" | |
235 | "xesam:totalSpace" "xesam:totalUncompressedSize" "xesam:url" | |
236 | "xesam:usageIntensity" "xesam:userComment" "xesam:userKeyword" | |
ee7683eb PE |
237 | "xesam:uuid" "xesam:version" "xesam:verticalResolution" |
238 | "xesam:videoBitrate" | |
24008bc4 MA |
239 | "xesam:videoCodec" "xesam:videoCodecType" "xesam:whiteBalance" |
240 | "xesam:width" "xesam:wordCount" "xesam:workEmailAddress" | |
241 | "xesam:workPhoneNumber" "xesam:workPostalAddress" | |
242 | "xesam:yahooContactMedium") | |
243 | "All fields from the Xesam Core Ontology. | |
244 | This defconst can be used to check for a new search engine, which | |
245 | fields are supported.") | |
246 | ||
247 | (defconst xesam-user-query | |
248 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?> | |
249 | <request xmlns=\"http://freedesktop.org/standards/xesam/1.0/query\"> | |
250 | <userQuery> | |
251 | %s | |
252 | </userQuery> | |
253 | </request>" | |
254 | "The Xesam user query XML.") | |
255 | ||
256 | (defconst xesam-fulltext-query | |
257 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?> | |
258 | <request xmlns=\"http://freedesktop.org/standards/xesam/1.0/query\"> | |
259 | <query content=\"xesam:Document\" source=\"xesam:File\"> | |
260 | <fullText> | |
261 | <string>%s</string> | |
262 | </fullText> | |
263 | </query> | |
264 | </request>" | |
265 | "The Xesam fulltext query XML.") | |
266 | ||
f0e35aeb GM |
267 | (declare-function dbus-get-unique-name "dbusbind.c" (bus)) |
268 | ||
818f12ce | 269 | (defvar xesam-dbus-unique-names |
7764286e MA |
270 | (ignore-errors |
271 | (list (cons :system (dbus-get-unique-name :system)) | |
272 | (cons :session (dbus-get-unique-name :session)))) | |
818f12ce MA |
273 | "The unique names, under which Emacs is registered at D-Bus.") |
274 | ||
275 | (defun xesam-dbus-call-method (&rest args) | |
276 | "Apply a D-Bus method call. | |
fa463103 PE |
277 | `dbus-call-method' is preferred, because it performs better. |
278 | If the target D-Bus service is owned by Emacs, this | |
818f12ce MA |
279 | is not applicable, and `dbus-call-method-non-blocking' must be |
280 | used instead. ARGS are identical to the argument list of both | |
281 | functions." | |
282 | (apply | |
283 | ;; The first argument is the bus, the second argument the targt service. | |
284 | (if (string-equal (cdr (assoc (car args) xesam-dbus-unique-names)) | |
285 | (cadr args)) | |
286 | 'dbus-call-method-non-blocking 'dbus-call-method) | |
287 | args)) | |
288 | ||
24008bc4 MA |
289 | (defun xesam-get-property (engine property) |
290 | "Return the PROPERTY value of ENGINE." | |
291 | ;; "GetProperty" returns a variant, so we must use the car. | |
818f12ce | 292 | (car (xesam-dbus-call-method |
24008bc4 MA |
293 | :session (car engine) xesam-path-search |
294 | xesam-interface-search "GetProperty" | |
818f12ce | 295 | (xesam-get-cached-property engine "session") property))) |
24008bc4 MA |
296 | |
297 | (defun xesam-set-property (engine property value) | |
298 | "Set the PROPERTY of ENGINE to VALUE. | |
299 | VALUE can be a string, a non-negative integer, a boolean | |
300 | value (nil or t), or a list of them. It returns the new value of | |
301 | PROPERTY in the search engine. This new value can be different | |
302 | from VALUE, depending on what the search engine accepts." | |
303 | ;; "SetProperty" returns a variant, so we must use the car. | |
818f12ce | 304 | (car (xesam-dbus-call-method |
24008bc4 MA |
305 | :session (car engine) xesam-path-search |
306 | xesam-interface-search "SetProperty" | |
818f12ce | 307 | (xesam-get-cached-property engine "session") property |
24008bc4 MA |
308 | ;; The value must be a variant. It can be only a string, an |
309 | ;; unsigned int, a boolean, or an array of them. So we need | |
310 | ;; no type keyword; we let the type check to the search | |
311 | ;; engine. | |
312 | (list :variant value)))) | |
313 | ||
314 | (defvar xesam-minibuffer-vendor-history nil | |
315 | "Interactive vendor history.") | |
316 | ||
317 | (defvar xesam-minibuffer-query-history nil | |
318 | "Interactive query history.") | |
319 | ||
320 | ;; Pacify byte compiler. | |
96038f81 MA |
321 | (defvar xesam-vendor nil) |
322 | (make-variable-buffer-local 'xesam-vendor) | |
323 | (put 'xesam-vendor 'permanent-local t) | |
324 | ||
24008bc4 MA |
325 | (defvar xesam-engine nil) |
326 | (defvar xesam-search nil) | |
5dd33078 MA |
327 | (defvar xesam-type nil) |
328 | (defvar xesam-query nil) | |
329 | (defvar xesam-xml-string nil) | |
4edefb46 | 330 | (defvar xesam-objects nil) |
24008bc4 MA |
331 | (defvar xesam-current nil) |
332 | (defvar xesam-count nil) | |
24008bc4 | 333 | (defvar xesam-to nil) |
96038f81 | 334 | (defvar xesam-notify-function nil) |
24008bc4 MA |
335 | (defvar xesam-refreshing nil) |
336 | ||
337 | \f | |
338 | ;;; Search engines. | |
339 | ||
340 | (defvar xesam-search-engines nil | |
341 | "List of available Xesam search engines. | |
818f12ce MA |
342 | Every entry is an association list, with a car denoting the |
343 | unique D-Bus service name of the engine. The rest of the entry | |
344 | are cached associations of engine attributes, like the session | |
345 | identifier, and the display name. Example: | |
346 | ||
347 | \(\(\":1.59\" | |
348 | \(\"session\" . \"0t1214948020ut358230u0p2698r3912347765k3213849828\") | |
349 | \(\"vendor.display\" . \"Tracker Xesam Service\")) | |
350 | \(\":1.27\" | |
351 | \(\"session\" . \"strigisession1369133069\") | |
352 | \(\"vendor.display\" . \"Strigi Desktop Search\"))) | |
24008bc4 MA |
353 | |
354 | A Xesam-compatible search engine is identified as a queued D-Bus | |
818f12ce MA |
355 | service of the known service `xesam-service-search'.") |
356 | ||
357 | (defun xesam-get-cached-property (engine property) | |
358 | "Return the PROPERTY value of ENGINE from the cache. | |
359 | If PROPERTY is not existing, retrieve it from ENGINE first." | |
360 | ;; If the property has not been cached yet, we retrieve it from the | |
361 | ;; engine, and cache it. | |
362 | (unless (assoc property engine) | |
363 | (xesam-set-cached-property | |
364 | engine property (xesam-get-property engine property))) | |
365 | (cdr (assoc property engine))) | |
366 | ||
367 | (defun xesam-set-cached-property (engine property value) | |
368 | "Set the PROPERTY of ENGINE to VALUE in the cache." | |
369 | (setcdr engine (append (cdr engine) (list (cons property value))))) | |
24008bc4 MA |
370 | |
371 | (defun xesam-delete-search-engine (&rest args) | |
818f12ce MA |
372 | "Remove service from `xesam-search-engines'." |
373 | (setq xesam-search-engines | |
374 | (delete (assoc (car args) xesam-search-engines) xesam-search-engines))) | |
24008bc4 | 375 | |
f0e35aeb GM |
376 | (defvar dbus-debug) |
377 | ||
24008bc4 MA |
378 | (defun xesam-search-engines () |
379 | "Return Xesam search engines, stored in `xesam-search-engines'. | |
380 | The first search engine is the name owner of `xesam-service-search'. | |
381 | If there is no registered search engine at all, the function returns `nil'." | |
382 | (let ((services (dbus-ignore-errors | |
383 | (dbus-list-queued-owners | |
384 | :session xesam-service-search))) | |
385 | engine vendor-id hit-fields) | |
386 | (dolist (service services) | |
387 | (unless (assoc-string service xesam-search-engines) | |
388 | ||
389 | ;; Open a new session, and add it to the search engines list. | |
818f12ce MA |
390 | (add-to-list 'xesam-search-engines (list service) 'append) |
391 | (setq engine (assoc service xesam-search-engines)) | |
392 | ||
393 | ;; Add the session string. | |
394 | (xesam-set-cached-property | |
395 | engine "session" | |
396 | (xesam-dbus-call-method | |
397 | :session service xesam-path-search | |
398 | xesam-interface-search "NewSession")) | |
399 | ||
400 | ;; Unset the "search.live" property; we don't want to be | |
401 | ;; informed by changed results. | |
402 | (xesam-set-property engine "search.live" nil) | |
24008bc4 MA |
403 | |
404 | ;; Check the vendor properties. | |
405 | (setq vendor-id (xesam-get-property engine "vendor.id") | |
406 | hit-fields (xesam-get-property engine "hit.fields")) | |
407 | ||
8350f087 | 408 | ;; Usually, `hit.fields' shall describe supported fields. |
24008bc4 MA |
409 | ;; That is not the case now, so we set it ourselves. |
410 | ;; Hopefully, this will change later. | |
411 | (setq hit-fields | |
a464a6c7 SM |
412 | (pcase (intern vendor-id) |
413 | (`Beagle | |
3cf235eb | 414 | '("xesam:mimeType" "xesam:url")) |
a464a6c7 | 415 | (`Strigi |
3cf235eb MA |
416 | '("xesam:author" "xesam:cc" "xesam:charset" |
417 | "xesam:contentType" "xesam:fileExtension" | |
418 | "xesam:id" "xesam:lineCount" "xesam:links" | |
419 | "xesam:mimeType" "xesam:name" "xesam:size" | |
420 | "xesam:sourceModified" "xesam:subject" "xesam:to" | |
421 | "xesam:url")) | |
a464a6c7 | 422 | (`TrackerXesamSession |
3cf235eb | 423 | '("xesam:relevancyRating" "xesam:url")) |
a464a6c7 | 424 | (`Debbugs |
3cf235eb MA |
425 | '("xesam:keyword" "xesam:owner" "xesam:title" |
426 | "xesam:url" "xesam:sourceModified" "xesam:mimeType" | |
427 | "debbugs:key")) | |
428 | ;; xesam-tools yahoo service. | |
a464a6c7 | 429 | (_ '("xesam:contentModified" "xesam:mimeType" "xesam:summary" |
3cf235eb | 430 | "xesam:title" "xesam:url" "yahoo:displayUrl")))) |
24008bc4 MA |
431 | |
432 | (xesam-set-property engine "hit.fields" hit-fields) | |
433 | (xesam-set-property engine "hit.fields.extended" '("xesam:snippet")) | |
434 | ||
435 | ;; Let us notify, when the search engine disappears. | |
436 | (dbus-register-signal | |
437 | :session dbus-service-dbus dbus-path-dbus | |
438 | dbus-interface-dbus "NameOwnerChanged" | |
439 | 'xesam-delete-search-engine service)))) | |
440 | xesam-search-engines) | |
441 | ||
442 | \f | |
443 | ;;; Search buffers. | |
444 | ||
abef340a SS |
445 | (defvar xesam-mode-map |
446 | (let ((map (copy-keymap special-mode-map))) | |
2df41f9c | 447 | (set-keymap-parent map widget-keymap) |
abef340a SS |
448 | map)) |
449 | ||
450 | (define-derived-mode xesam-mode special-mode "Xesam" | |
24008bc4 MA |
451 | "Major mode for presenting search results of a Xesam search. |
452 | In this mode, widgets represent the search results. | |
453 | ||
454 | \\{xesam-mode-map} | |
96038f81 MA |
455 | Turning on Xesam mode runs the normal hook `xesam-mode-hook'. It |
456 | can be used to set `xesam-notify-function', which must a search | |
457 | engine specific, widget :notify function to visualize xesam:url." | |
458 | (set (make-local-variable 'xesam-notify-function) nil) | |
5dd33078 MA |
459 | ;; Maybe we implement something useful, later on. |
460 | (set (make-local-variable 'revert-buffer-function) 'ignore) | |
461 | ;; `xesam-engine', `xesam-search', `xesam-type', `xesam-query', and | |
462 | ;; `xesam-xml-string' will be set in `xesam-new-search'. | |
463 | (set (make-local-variable 'xesam-engine) nil) | |
464 | (set (make-local-variable 'xesam-search) nil) | |
465 | (set (make-local-variable 'xesam-type) "") | |
466 | (set (make-local-variable 'xesam-query) "") | |
467 | (set (make-local-variable 'xesam-xml-string) "") | |
4edefb46 | 468 | (set (make-local-variable 'xesam-objects) nil) |
5dd33078 MA |
469 | ;; `xesam-current' is the last hit put into the search buffer, |
470 | (set (make-local-variable 'xesam-current) 0) | |
471 | ;; `xesam-count' is the number of hits reported by the search engine. | |
472 | (set (make-local-variable 'xesam-count) 0) | |
473 | ;; `xesam-to' is the upper hit number to be presented. | |
474 | (set (make-local-variable 'xesam-to) xesam-hits-per-page) | |
96038f81 MA |
475 | ;; `xesam-notify-function' can be a search engine specific function |
476 | ;; to visualize xesam:url. It can be overwritten in `xesam-mode'. | |
477 | (set (make-local-variable 'xesam-notify-function) nil) | |
5dd33078 MA |
478 | ;; `xesam-refreshing' is an indicator, whether the buffer is just |
479 | ;; being updated. Needed, because `xesam-refresh-search-buffer' | |
480 | ;; can be triggered by an event. | |
481 | (set (make-local-variable 'xesam-refreshing) nil) | |
482 | ;; Mode line position returns hit counters. | |
483 | (set (make-local-variable 'mode-line-position) | |
484 | (list '(-3 "%p%") | |
485 | '(10 (:eval (format " (%d/%d)" xesam-current xesam-count))))) | |
486 | ;; Header line contains the query string. | |
487 | (set (make-local-variable 'header-line-format) | |
488 | (list '(20 | |
489 | (:eval | |
490 | (list "Type: " | |
4edefb46 | 491 | (propertize xesam-type 'face 'xesam-mode-line)))) |
5dd33078 MA |
492 | '(10 |
493 | (:eval | |
494 | (list " Query: " | |
495 | (propertize | |
496 | xesam-query | |
4edefb46 | 497 | 'face 'xesam-mode-line |
818f12ce | 498 | 'help-echo (when xesam-debug xesam-xml-string))))))) |
5dd33078 | 499 | |
32226619 | 500 | (when (not (called-interactively-p 'interactive)) |
de8bb89e MA |
501 | ;; Initialize buffer. |
502 | (setq buffer-read-only t) | |
503 | (let ((inhibit-read-only t)) | |
5dd33078 MA |
504 | (erase-buffer)))) |
505 | ||
506 | ;; It doesn't make sense to call it interactively. | |
507 | (put 'xesam-mode 'disabled t) | |
24008bc4 | 508 | |
4edefb46 MA |
509 | ;; The very first buffer created with `xesam-mode' does not have the |
510 | ;; keymap etc. So we create a dummy buffer. Stupid. | |
511 | (with-temp-buffer (xesam-mode)) | |
512 | ||
17668903 MA |
513 | (define-minor-mode xesam-minor-mode |
514 | "Toggle Xesam minor mode. | |
ac6c8639 CY |
515 | With a prefix argument ARG, enable Xesam minor mode if ARG is |
516 | positive, and disable it otherwise. If called from Lisp, enable | |
517 | the mode if ARG is omitted or nil. | |
17668903 MA |
518 | |
519 | When Xesam minor mode is enabled, all text which matches a | |
520 | previous Xesam query in this buffer is highlighted." | |
521 | :group 'xesam | |
522 | :init-value nil | |
523 | :lighter " Xesam" | |
524 | (when (local-variable-p 'xesam-query) | |
525 | ;; Run only if the buffer is related to a Xesam search. | |
526 | (save-excursion | |
527 | (if xesam-minor-mode | |
528 | ;; Highlight hits. | |
529 | (let ((query-regexp (regexp-opt (split-string xesam-query nil t) t)) | |
530 | (case-fold-search t)) | |
531 | ;; I have no idea whether people will like setting | |
532 | ;; `isearch-case-fold-search' and `query-regexp'. Maybe | |
533 | ;; this shall be controlled by a custom option. | |
534 | (unless isearch-case-fold-search (isearch-toggle-case-fold)) | |
535 | (isearch-update-ring query-regexp t) | |
536 | ;; Create overlays. | |
537 | (goto-char (point-min)) | |
538 | (while (re-search-forward query-regexp nil t) | |
539 | (overlay-put | |
540 | (make-overlay | |
541 | (match-beginning 0) (match-end 0)) 'face 'xesam-highlight))) | |
542 | ;; Remove overlays. | |
543 | (dolist (ov (overlays-in (point-min) (point-max))) | |
544 | (delete-overlay ov)))))) | |
545 | ||
24008bc4 MA |
546 | (defun xesam-buffer-name (service search) |
547 | "Return the buffer name where to present search results. | |
548 | SERVICE is the D-Bus unique service name of the Xesam search engine. | |
549 | SEARCH is the search identification in that engine. Both must be strings." | |
550 | (format "*%s/%s*" service search)) | |
551 | ||
4edefb46 | 552 | (defun xesam-highlight-string (string) |
f7a17e30 MA |
553 | "Highlight text enclosed by <b> and </b>. |
554 | Return propertized STRING." | |
4edefb46 MA |
555 | (while (string-match "\\(.*\\)\\(<b>\\)\\(.*\\)\\(</b>\\)\\(.*\\)" string) |
556 | (setq string | |
557 | (format | |
558 | "%s%s%s" | |
559 | (match-string 1 string) | |
560 | (propertize (match-string 3 string) 'face 'xesam-highlight) | |
561 | (match-string 5 string)))) | |
562 | string) | |
563 | ||
564 | (defun xesam-refresh-entry (engine entry) | |
24008bc4 | 565 | "Refreshes one entry in the search buffer." |
4edefb46 | 566 | (let* ((result (nth (1- xesam-current) xesam-objects)) |
24008bc4 MA |
567 | widget) |
568 | ||
569 | ;; Create widget. | |
570 | (setq widget (widget-convert 'link)) | |
de8bb89e MA |
571 | (when xesam-debug |
572 | (widget-put widget :help-echo "")) | |
24008bc4 MA |
573 | |
574 | ;; Take all results. | |
818f12ce | 575 | (dolist (field (xesam-get-cached-property engine "hit.fields")) |
4edefb46 MA |
576 | (when (cond |
577 | ((stringp (caar result)) (not (zerop (length (caar result))))) | |
578 | ((numberp (caar result)) (not (zerop (caar result)))) | |
579 | ((caar result) t)) | |
24008bc4 | 580 | (when xesam-debug |
de8bb89e MA |
581 | (widget-put |
582 | widget :help-echo | |
583 | (format "%s%s: %s\n" | |
584 | (widget-get widget :help-echo) field (caar result)))) | |
24008bc4 MA |
585 | (widget-put widget (intern (concat ":" field)) (caar result))) |
586 | (setq result (cdr result))) | |
587 | ||
588 | ;; Strigi doesn't return URLs in xesam:url. We must fix this. | |
589 | (when | |
590 | (not (url-type (url-generic-parse-url (widget-get widget :xesam:url)))) | |
591 | (widget-put | |
592 | widget :xesam:url (concat "file://" (widget-get widget :xesam:url)))) | |
593 | ||
4edefb46 MA |
594 | ;; Strigi returns xesam:size as string. We must fix this. |
595 | (when (and (widget-member widget :xesam:size) | |
596 | (stringp (widget-get widget :xesam:size))) | |
597 | (widget-put | |
598 | widget :xesam:size (string-to-number (widget-get widget :xesam:url)))) | |
599 | ||
24008bc4 MA |
600 | ;; First line: :tag. |
601 | (cond | |
602 | ((widget-member widget :xesam:title) | |
603 | (widget-put widget :tag (widget-get widget :xesam:title))) | |
604 | ((widget-member widget :xesam:subject) | |
605 | (widget-put widget :tag (widget-get widget :xesam:subject))) | |
606 | ((widget-member widget :xesam:mimeType) | |
607 | (widget-put widget :tag (widget-get widget :xesam:mimeType))) | |
608 | ((widget-member widget :xesam:name) | |
609 | (widget-put widget :tag (widget-get widget :xesam:name)))) | |
610 | ||
4edefb46 MA |
611 | ;; Highlight the search items. |
612 | (when (widget-member widget :tag) | |
613 | (widget-put | |
614 | widget :tag (xesam-highlight-string (widget-get widget :tag)))) | |
615 | ||
24008bc4 | 616 | ;; Last Modified. |
f7a17e30 MA |
617 | (when (and (widget-member widget :xesam:sourceModified) |
618 | (not | |
619 | (zerop | |
620 | (string-to-number (widget-get widget :xesam:sourceModified))))) | |
24008bc4 MA |
621 | (widget-put |
622 | widget :tag | |
623 | (format | |
624 | "%s\nLast Modified: %s" | |
625 | (or (widget-get widget :tag) "") | |
626 | (format-time-string | |
627 | "%d %B %Y, %T" | |
628 | (seconds-to-time | |
629 | (string-to-number (widget-get widget :xesam:sourceModified))))))) | |
630 | ||
631 | ;; Second line: :value. | |
632 | (widget-put widget :value (widget-get widget :xesam:url)) | |
633 | ||
634 | (cond | |
96038f81 MA |
635 | ;; A search engine can set `xesam-notify-function' via |
636 | ;; `xesam-mode-hooks'. | |
637 | (xesam-notify-function | |
638 | (widget-put widget :notify xesam-notify-function)) | |
639 | ||
24008bc4 MA |
640 | ;; In case of HTML, we use a URL link. |
641 | ((and (widget-member widget :xesam:mimeType) | |
642 | (string-equal "text/html" (widget-get widget :xesam:mimeType))) | |
643 | (setcar widget 'url-link)) | |
644 | ||
645 | ;; For local files, we will open the file as default action. | |
646 | ((string-match "file" | |
647 | (url-type (url-generic-parse-url | |
648 | (widget-get widget :xesam:url)))) | |
649 | (widget-put | |
650 | widget :notify | |
c7041c35 | 651 | (lambda (widget &rest ignore) |
f7a17e30 MA |
652 | (let ((query xesam-query)) |
653 | (find-file | |
654 | (url-filename (url-generic-parse-url (widget-value widget)))) | |
17668903 MA |
655 | (set (make-local-variable 'xesam-query) query) |
656 | (xesam-minor-mode 1)))) | |
24008bc4 MA |
657 | (widget-put |
658 | widget :value | |
659 | (url-filename (url-generic-parse-url (widget-get widget :xesam:url)))))) | |
660 | ||
661 | ;; Third line: :doc. | |
662 | (cond | |
663 | ((widget-member widget :xesam:summary) | |
664 | (widget-put widget :doc (widget-get widget :xesam:summary))) | |
665 | ((widget-member widget :xesam:snippet) | |
666 | (widget-put widget :doc (widget-get widget :xesam:snippet)))) | |
667 | ||
668 | (when (widget-member widget :doc) | |
24008bc4 | 669 | (with-temp-buffer |
4edefb46 MA |
670 | (insert |
671 | (xesam-highlight-string (widget-get widget :doc))) | |
24008bc4 | 672 | (fill-region-as-paragraph (point-min) (point-max)) |
4edefb46 MA |
673 | (widget-put widget :doc (buffer-string))) |
674 | (widget-put widget :help-echo (widget-get widget :doc))) | |
24008bc4 MA |
675 | |
676 | ;; Format the widget. | |
677 | (widget-put | |
678 | widget :format | |
de8bb89e | 679 | (format "%d. %s%%[%%v%%]\n%s\n" xesam-current |
24008bc4 MA |
680 | (if (widget-member widget :tag) "%{%t%}\n" "") |
681 | (if (widget-member widget :doc) "%h" ""))) | |
682 | ||
683 | ;; Write widget. | |
684 | (goto-char (point-max)) | |
685 | (widget-default-create widget) | |
686 | (set-buffer-modified-p nil) | |
de8bb89e | 687 | (force-mode-line-update) |
24008bc4 MA |
688 | (redisplay))) |
689 | ||
4edefb46 MA |
690 | (defun xesam-get-hits (engine search hits) |
691 | "Retrieve hits from ENGINE." | |
692 | (with-current-buffer (xesam-buffer-name (car engine) search) | |
693 | (setq xesam-objects | |
694 | (append xesam-objects | |
695 | (xesam-dbus-call-method | |
696 | :session (car engine) xesam-path-search | |
697 | xesam-interface-search "GetHits" search hits))))) | |
698 | ||
24008bc4 MA |
699 | (defun xesam-refresh-search-buffer (engine search) |
700 | "Refreshes the buffer, presenting results of SEARCH." | |
701 | (with-current-buffer (xesam-buffer-name (car engine) search) | |
702 | ;; Work only if nobody else is here. | |
4edefb46 | 703 | (unless (or xesam-refreshing (>= xesam-current xesam-to)) |
24008bc4 MA |
704 | (setq xesam-refreshing t) |
705 | (unwind-protect | |
4edefb46 MA |
706 | (let (widget) |
707 | ||
708 | ;; Retrieve needed hits for visualization. | |
709 | (while (> (min xesam-to xesam-count) (length xesam-objects)) | |
710 | (xesam-get-hits | |
711 | engine search | |
712 | (min xesam-hits-per-page | |
713 | (- (min xesam-to xesam-count) (length xesam-objects))))) | |
714 | ||
715 | ;; Add all result widgets. | |
de8bb89e | 716 | (while (< xesam-current (min xesam-to xesam-count)) |
4edefb46 | 717 | (setq xesam-current (1+ xesam-current)) |
de8bb89e | 718 | (xesam-refresh-entry engine search)) |
24008bc4 MA |
719 | |
720 | ;; Add "NEXT" widget. | |
4edefb46 | 721 | (when (> xesam-count xesam-to) |
24008bc4 MA |
722 | (goto-char (point-max)) |
723 | (widget-create | |
724 | 'link | |
725 | :notify | |
c7041c35 MA |
726 | (lambda (widget &rest ignore) |
727 | (setq xesam-to (+ xesam-to xesam-hits-per-page)) | |
728 | (widget-delete widget) | |
729 | (xesam-refresh-search-buffer xesam-engine xesam-search)) | |
24008bc4 | 730 | "NEXT") |
4edefb46 MA |
731 | (widget-beginning-of-line)) |
732 | ||
733 | ;; Prefetch next hits. | |
734 | (when (> (min (+ xesam-hits-per-page xesam-to) xesam-count) | |
735 | (length xesam-objects)) | |
736 | (xesam-get-hits | |
737 | engine search | |
738 | (min xesam-hits-per-page | |
739 | (- (min (+ xesam-hits-per-page xesam-to) xesam-count) | |
c7041c35 | 740 | (length xesam-objects))))) |
24008bc4 | 741 | |
3cf235eb MA |
742 | ;; Add "DONE" widget. |
743 | (when (= xesam-current xesam-count) | |
744 | (goto-char (point-max)) | |
745 | (widget-create 'link :notify 'ignore "DONE") | |
c7041c35 | 746 | (widget-beginning-of-line))) |
3cf235eb | 747 | |
24008bc4 MA |
748 | ;; Return with save settings. |
749 | (setq xesam-refreshing nil))))) | |
750 | ||
751 | \f | |
752 | ;;; Search functions. | |
753 | ||
754 | (defun xesam-signal-handler (&rest args) | |
755 | "Handles the different D-Bus signals of a Xesam search." | |
756 | (let* ((service (dbus-event-service-name last-input-event)) | |
757 | (member (dbus-event-member-name last-input-event)) | |
758 | (search (nth 0 args)) | |
759 | (buffer (xesam-buffer-name service search))) | |
760 | ||
761 | (when (get-buffer buffer) | |
762 | (with-current-buffer buffer | |
763 | (cond | |
764 | ||
765 | ((string-equal member "HitsAdded") | |
de8bb89e | 766 | (setq xesam-count (+ xesam-count (nth 1 args))) |
24008bc4 MA |
767 | ;; We use `run-at-time' in order to not block the event queue. |
768 | (run-at-time | |
769 | 0 nil | |
770 | 'xesam-refresh-search-buffer | |
771 | (assoc service xesam-search-engines) search)) | |
772 | ||
773 | ((string-equal member "SearchDone") | |
774 | (setq mode-line-process | |
4edefb46 | 775 | (propertize " Done" 'face 'xesam-mode-line)) |
24008bc4 MA |
776 | (force-mode-line-update))))))) |
777 | ||
4edefb46 MA |
778 | (defun xesam-kill-buffer-function () |
779 | "Send the CloseSearch indication." | |
780 | (when (and (eq major-mode 'xesam-mode) (stringp xesam-search)) | |
30fe660e MA |
781 | (ignore-errors ;; The D-Bus service could have disappeared. |
782 | (xesam-dbus-call-method | |
783 | :session (car xesam-engine) xesam-path-search | |
784 | xesam-interface-search "CloseSearch" xesam-search)))) | |
4edefb46 | 785 | |
5dd33078 | 786 | (defun xesam-new-search (engine type query) |
24008bc4 | 787 | "Create a new search session. |
5dd33078 MA |
788 | ENGINE identifies the search engine. TYPE is the query type, it |
789 | can be either `fulltext-query', or `user-query'. QUERY is a | |
790 | string in the Xesam query language. A string, identifying the | |
791 | search, is returned." | |
24008bc4 | 792 | (let* ((service (car engine)) |
818f12ce | 793 | (session (xesam-get-cached-property engine "session")) |
5dd33078 MA |
794 | (xml-string |
795 | (format | |
796 | (if (eq type 'user-query) xesam-user-query xesam-fulltext-query) | |
c7041c35 | 797 | (url-insert-entities-in-string query))) |
818f12ce | 798 | (search (xesam-dbus-call-method |
24008bc4 | 799 | :session service xesam-path-search |
5dd33078 | 800 | xesam-interface-search "NewSearch" session xml-string))) |
de8bb89e | 801 | |
24008bc4 MA |
802 | ;; Let us notify for relevant signals. We ignore "HitsRemoved", |
803 | ;; "HitsModified" and "StateChanged"; there is nothing to do for | |
804 | ;; us. | |
805 | (dbus-register-signal | |
806 | :session service xesam-path-search | |
807 | xesam-interface-search "HitsAdded" | |
808 | 'xesam-signal-handler search) | |
809 | (dbus-register-signal | |
810 | :session service xesam-path-search | |
811 | xesam-interface-search "SearchDone" | |
812 | 'xesam-signal-handler search) | |
de8bb89e | 813 | |
24008bc4 MA |
814 | ;; Create the search buffer. |
815 | (with-current-buffer | |
816 | (generate-new-buffer (xesam-buffer-name service search)) | |
817 | (switch-to-buffer-other-window (current-buffer)) | |
e4920bc9 | 818 | ;; Initialize buffer with `xesam-mode'. `xesam-vendor' must be |
96038f81 MA |
819 | ;; set before calling `xesam-mode', because we want to give the |
820 | ;; hook functions a chance to identify their search engine. | |
821 | (setq xesam-vendor (xesam-get-cached-property engine "vendor.id")) | |
24008bc4 MA |
822 | (xesam-mode) |
823 | (setq xesam-engine engine | |
824 | xesam-search search | |
5dd33078 MA |
825 | ;; `xesam-type', `xesam-query' and `xesam-xml-string' |
826 | ;; are displayed in the header line. | |
827 | xesam-type (symbol-name type) | |
828 | xesam-query query | |
829 | xesam-xml-string xml-string | |
4edefb46 | 830 | xesam-objects nil |
de8bb89e MA |
831 | ;; The buffer identification shall indicate the search |
832 | ;; engine. The `help-echo' property is used for debug | |
833 | ;; information, when applicable. | |
24008bc4 | 834 | mode-line-buffer-identification |
de8bb89e | 835 | (if (not xesam-debug) |
96038f81 | 836 | (list 12 (propertized-buffer-identification xesam-vendor)) |
de8bb89e | 837 | (propertize |
96038f81 | 838 | xesam-vendor |
818f12ce | 839 | 'help-echo |
de8bb89e | 840 | (mapconcat |
c7041c35 MA |
841 | (lambda (x) |
842 | (format "%s: %s" x (xesam-get-cached-property engine x))) | |
de8bb89e MA |
843 | '("vendor.id" "vendor.version" "vendor.display" "vendor.xesam" |
844 | "vendor.ontology.fields" "vendor.ontology.contents" | |
845 | "vendor.ontology.sources" "vendor.extensions" | |
846 | "vendor.ontologies" "vendor.maxhits") | |
5dd33078 | 847 | "\n")))) |
4edefb46 MA |
848 | (add-hook 'kill-buffer-hook 'xesam-kill-buffer-function) |
849 | (force-mode-line-update)) | |
5dd33078 MA |
850 | |
851 | ;; Start the search. | |
818f12ce | 852 | (xesam-dbus-call-method |
5dd33078 MA |
853 | :session (car engine) xesam-path-search |
854 | xesam-interface-search "StartSearch" search) | |
24008bc4 MA |
855 | |
856 | ;; Return search id. | |
857 | search)) | |
858 | ||
3cf235eb | 859 | ;;;###autoload |
24008bc4 MA |
860 | (defun xesam-search (engine query) |
861 | "Perform an interactive search. | |
862 | ENGINE is the Xesam search engine to be applied, it must be one of the | |
863 | entries of `xesam-search-engines'. QUERY is the search string in the | |
864 | Xesam user query language. If the search engine does not support | |
865 | the Xesam user query language, a Xesam fulltext search is applied. | |
866 | ||
867 | The default search engine is the first entry in `xesam-search-engines'. | |
868 | Example: | |
869 | ||
870 | (xesam-search (car (xesam-search-engines)) \"emacs\")" | |
871 | (interactive | |
872 | (let* ((vendors (mapcar | |
c7041c35 | 873 | (lambda (x) (xesam-get-cached-property x "vendor.display")) |
24008bc4 MA |
874 | (xesam-search-engines))) |
875 | (vendor | |
876 | (if (> (length vendors) 1) | |
877 | (completing-read | |
878 | "Enter search engine: " vendors nil t | |
879 | (try-completion "" vendors) 'xesam-minibuffer-vendor-history) | |
880 | (car vendors)))) | |
881 | (list | |
882 | ;; ENGINE. | |
883 | (when vendor | |
884 | (dolist (elt (xesam-search-engines) engine) | |
818f12ce MA |
885 | (when (string-equal |
886 | (xesam-get-cached-property elt "vendor.display") vendor) | |
24008bc4 MA |
887 | (setq engine elt)))) |
888 | ;; QUERY. | |
889 | (when vendor | |
890 | (read-from-minibuffer | |
891 | "Enter search string: " nil nil nil | |
892 | 'xesam-minibuffer-query-history))))) | |
893 | ||
5dd33078 MA |
894 | (if (null engine) |
895 | (message "No search engine running") | |
896 | (if (zerop (length query)) | |
897 | (message "No query applied") | |
898 | (xesam-new-search engine xesam-query-type query)))) | |
24008bc4 MA |
899 | |
900 | (provide 'xesam) | |
901 | ||
902 | ;;; TODO: | |
903 | ||
f7a17e30 | 904 | ;; * Buffer highlighting needs better analysis of query string. |
4edefb46 | 905 | ;; * Accept input while retrieving prefetched hits. `run-at-time'? |
818f12ce | 906 | ;; * With prefix, let's choose search engine. |
24008bc4 | 907 | ;; * Minibuffer completion for user queries. |
de8bb89e | 908 | ;; * `revert-buffer-function' implementation. |
de8bb89e | 909 | ;; |
24008bc4 MA |
910 | ;; * Mid term |
911 | ;; - If available, use ontologies for field selection. | |
912 | ;; - Search engines for Emacs bugs database, wikipedia, google, | |
913 | ;; yahoo, ebay, ... | |
4edefb46 | 914 | ;; - Construct complex queries via widgets, like in mairix.el. |
24008bc4 MA |
915 | |
916 | ;;; xesam.el ends here |