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