diff options
| author | Torsten Hilbrich | 2020-10-05 06:50:25 +0200 |
|---|---|---|
| committer | Torsten Hilbrich | 2020-10-08 05:56:31 +0200 |
| commit | b6227446d9166130cf6d30b0fc11428fe001c90c (patch) | |
| tree | 5e3826e8071647aabb3e29b4f6c97928ee959e02 | |
| parent | 0a5e9cf2622a0282d56cc150af5a94b5d5fd71be (diff) | |
| download | emacs-b6227446d9166130cf6d30b0fc11428fe001c90c.tar.gz emacs-b6227446d9166130cf6d30b0fc11428fe001c90c.zip | |
Importing dictionary module
* lisp/net: Adding files connection.el, link.el, dictionary.el,
imported from https://github.com/myrkr/dictionary-el.git
| -rw-r--r-- | lisp/net/connection.el | 159 | ||||
| -rw-r--r-- | lisp/net/dictionary.el | 1367 | ||||
| -rw-r--r-- | lisp/net/link.el | 129 |
3 files changed, 1655 insertions, 0 deletions
diff --git a/lisp/net/connection.el b/lisp/net/connection.el new file mode 100644 index 00000000000..3afcc2cb894 --- /dev/null +++ b/lisp/net/connection.el | |||
| @@ -0,0 +1,159 @@ | |||
| 1 | ;;; connection.el --- TCP-based client connection | ||
| 2 | |||
| 3 | ;; Author: Torsten Hilbrich <torsten.hilbrich@gmx.net> | ||
| 4 | ;; Keywords: network | ||
| 5 | ;; Version: 1.11 | ||
| 6 | |||
| 7 | ;; This file is free software; you can redistribute it and/or modify | ||
| 8 | ;; it under the terms of the GNU General Public License as published by | ||
| 9 | ;; the Free Software Foundation; either version 2, or (at your option) | ||
| 10 | ;; any later version. | ||
| 11 | |||
| 12 | ;; This file is distributed in the hope that it will be useful, | ||
| 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | ;; GNU General Public License for more details. | ||
| 16 | |||
| 17 | ;; You should have received a copy of the GNU General Public License | ||
| 18 | ;; along with GNU Emacs; see the file COPYING. If not, write to | ||
| 19 | ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
| 20 | ;; Boston, MA 02111-1307, USA. | ||
| 21 | |||
| 22 | ;;; Commentary: | ||
| 23 | |||
| 24 | ;; connection allows to handle TCP-based connections in client mode | ||
| 25 | ;; where text-based information are exchanged. There is special | ||
| 26 | ;; support for handling CR LF (and the usual CR LF . CR LF | ||
| 27 | ;; terminater). | ||
| 28 | |||
| 29 | ;;; Code: | ||
| 30 | |||
| 31 | (eval-when-compile | ||
| 32 | (require 'cl)) | ||
| 33 | |||
| 34 | (defmacro connection-p (connection) | ||
| 35 | "Returns non-nil if `connection' is a connection object" | ||
| 36 | (list 'get connection ''connection)) | ||
| 37 | |||
| 38 | (defmacro connection-read-point (connection) | ||
| 39 | "Return the read point of the connection object." | ||
| 40 | (list 'get connection ''connection-read-point)) | ||
| 41 | |||
| 42 | (defmacro connection-process (connection) | ||
| 43 | "Return the process of the connection object." | ||
| 44 | (list 'get connection ''connection-process)) | ||
| 45 | |||
| 46 | (defmacro connection-buffer (connection) | ||
| 47 | "Return the buffer of the connection object." | ||
| 48 | (list 'get connection ''connection-buffer)) | ||
| 49 | |||
| 50 | (defmacro connection-set-read-point (connection point) | ||
| 51 | "Set the read-point for `connection' to `point'." | ||
| 52 | (list 'put connection ''connection-read-point point)) | ||
| 53 | |||
| 54 | (defmacro connection-set-process (connection process) | ||
| 55 | "Set the process for `connection' to `process'." | ||
| 56 | (list 'put connection ''connection-process process)) | ||
| 57 | |||
| 58 | (defmacro connection-set-buffer (connection buffer) | ||
| 59 | "Set the buffer for `connection' to `buffer'." | ||
| 60 | (list 'put connection ''connection-buffer buffer)) | ||
| 61 | |||
| 62 | (defun connection-create-data (buffer process point) | ||
| 63 | "Create a new connection data based on `buffer', `process', and `point'." | ||
| 64 | (let ((connection (make-symbol "connection"))) | ||
| 65 | (put connection 'connection t) | ||
| 66 | (connection-set-read-point connection point) | ||
| 67 | (connection-set-process connection process) | ||
| 68 | (connection-set-buffer connection buffer) | ||
| 69 | connection)) | ||
| 70 | |||
| 71 | (defun connection-open (server port) | ||
| 72 | "Open a connection to `server' and `port'. | ||
| 73 | A data structure identifing the connection is returned" | ||
| 74 | |||
| 75 | (let ((process-buffer (generate-new-buffer (format " connection to %s:%s" | ||
| 76 | server | ||
| 77 | port))) | ||
| 78 | (process)) | ||
| 79 | (with-current-buffer process-buffer | ||
| 80 | (setq process (open-network-stream "connection" process-buffer | ||
| 81 | server port)) | ||
| 82 | (connection-create-data process-buffer process (point-min))))) | ||
| 83 | |||
| 84 | (defun connection-status (connection) | ||
| 85 | "Return the status of the connection. | ||
| 86 | Possible return values are the symbols: | ||
| 87 | nil: argument is no connection object | ||
| 88 | 'none: argument has no connection | ||
| 89 | 'up: connection is open and buffer is existing | ||
| 90 | 'down: connection is closed | ||
| 91 | 'alone: connection is not associated with a buffer" | ||
| 92 | (if (connection-p connection) | ||
| 93 | (let ((process (connection-process connection)) | ||
| 94 | (buffer (connection-buffer connection))) | ||
| 95 | (if (not process) | ||
| 96 | 'none | ||
| 97 | (if (not (buffer-live-p buffer)) | ||
| 98 | 'alone | ||
| 99 | (if (not (eq (process-status process) 'open)) | ||
| 100 | 'down | ||
| 101 | 'up)))) | ||
| 102 | nil)) | ||
| 103 | |||
| 104 | (defun connection-close (connection) | ||
| 105 | "Force closing of the connection." | ||
| 106 | (if (connection-p connection) | ||
| 107 | (progn | ||
| 108 | (let ((buffer (connection-buffer connection)) | ||
| 109 | (process (connection-process connection))) | ||
| 110 | (if process | ||
| 111 | (delete-process process)) | ||
| 112 | (if buffer | ||
| 113 | (kill-buffer buffer)) | ||
| 114 | |||
| 115 | (connection-set-process connection nil) | ||
| 116 | (connection-set-buffer connection nil))))) | ||
| 117 | |||
| 118 | (defun connection-send (connection data) | ||
| 119 | "Send `data' to the process." | ||
| 120 | (unless (eq (connection-status connection) 'up) | ||
| 121 | (error "Connection is not up")) | ||
| 122 | (with-current-buffer (connection-buffer connection) | ||
| 123 | (goto-char (point-max)) | ||
| 124 | (connection-set-read-point connection (point)) | ||
| 125 | (process-send-string (connection-process connection) data))) | ||
| 126 | |||
| 127 | (defun connection-send-crlf (connection data) | ||
| 128 | "Send `data' together with CRLF to the process." | ||
| 129 | (connection-send connection (concat data "\r\n"))) | ||
| 130 | |||
| 131 | (defun connection-read (connection delimiter) | ||
| 132 | "Read data until `delimiter' is found inside the buffer." | ||
| 133 | (unless (eq (connection-status connection) 'up) | ||
| 134 | (error "Connection is not up")) | ||
| 135 | (let ((case-fold-search nil) | ||
| 136 | match-end) | ||
| 137 | (with-current-buffer (connection-buffer connection) | ||
| 138 | (goto-char (connection-read-point connection)) | ||
| 139 | ;; Wait until there is enough data | ||
| 140 | (while (not (search-forward-regexp delimiter nil t)) | ||
| 141 | (accept-process-output (connection-process connection) 3) | ||
| 142 | (goto-char (connection-read-point connection))) | ||
| 143 | (setq match-end (point)) | ||
| 144 | ;; Return the result | ||
| 145 | (let ((result (buffer-substring (connection-read-point connection) | ||
| 146 | match-end))) | ||
| 147 | (connection-set-read-point connection match-end) | ||
| 148 | result)))) | ||
| 149 | |||
| 150 | (defun connection-read-crlf (connection) | ||
| 151 | "Read until a line is completedx with CRLF" | ||
| 152 | (connection-read connection "\015?\012")) | ||
| 153 | |||
| 154 | (defun connection-read-to-point (connection) | ||
| 155 | "Read until a line is consisting of a single point" | ||
| 156 | (connection-read connection "\015?\012[.]\015?\012")) | ||
| 157 | |||
| 158 | (provide 'connection) | ||
| 159 | ;;; connection.el ends here | ||
diff --git a/lisp/net/dictionary.el b/lisp/net/dictionary.el new file mode 100644 index 00000000000..9545926cb25 --- /dev/null +++ b/lisp/net/dictionary.el | |||
| @@ -0,0 +1,1367 @@ | |||
| 1 | ;;; dictionary.el --- Client for rfc2229 dictionary servers | ||
| 2 | |||
| 3 | ;; Author: Torsten Hilbrich <torsten.hilbrich@gmx.net> | ||
| 4 | ;; Keywords: interface, dictionary | ||
| 5 | ;; Version: 1.11 | ||
| 6 | ;; Package-Requires: ((connection "1.11") (link "1.11")) | ||
| 7 | |||
| 8 | ;; This file is free software; you can redistribute it and/or modify | ||
| 9 | ;; it under the terms of the GNU General Public License as published by | ||
| 10 | ;; the Free Software Foundation; either version 2, or (at your option) | ||
| 11 | ;; any later version. | ||
| 12 | |||
| 13 | ;; This file is distributed in the hope that it will be useful, | ||
| 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | ;; GNU General Public License for more details. | ||
| 17 | |||
| 18 | ;; You should have received a copy of the GNU General Public License | ||
| 19 | ;; along with GNU Emacs; see the file COPYING. If not, write to | ||
| 20 | ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
| 21 | ;; Boston, MA 02111-1307, USA. | ||
| 22 | |||
| 23 | ;;; Commentary: | ||
| 24 | |||
| 25 | ;; dictionary allows you to interact with dictionary servers. | ||
| 26 | ;; Use M-x customize-group dictionary to modify user settings. | ||
| 27 | ;; | ||
| 28 | ;; Main functions for interaction are: | ||
| 29 | ;; dictionary - opens a new dictionary buffer | ||
| 30 | ;; dictionary-search - search for the definition of a word | ||
| 31 | ;; | ||
| 32 | ;; You can find more information in the README file of the GitHub | ||
| 33 | ;; repository https://github.com/myrkr/dictionary-el | ||
| 34 | |||
| 35 | ;;; Code: | ||
| 36 | |||
| 37 | (eval-when-compile | ||
| 38 | (require 'cl)) | ||
| 39 | |||
| 40 | (require 'easymenu) | ||
| 41 | (require 'custom) | ||
| 42 | (require 'connection) | ||
| 43 | (require 'link) | ||
| 44 | |||
| 45 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 46 | ;; Stuff for customizing. | ||
| 47 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 48 | |||
| 49 | (eval-when-compile | ||
| 50 | (unless (fboundp 'defface) | ||
| 51 | (message "Please update your custom.el file: %s" | ||
| 52 | "http://www.dina.kvl.dk/~abraham/custom/")) | ||
| 53 | |||
| 54 | (unless (fboundp 'defgroup) | ||
| 55 | (defmacro defgroup (&rest ignored)) | ||
| 56 | (defmacro defcustom (var value doc &rest ignored) | ||
| 57 | (list 'defvar var value doc)))) | ||
| 58 | |||
| 59 | (defvar dictionary-server) | ||
| 60 | (defun dictionary-set-server-var (name value) | ||
| 61 | (if (and (boundp 'dictionary-connection) | ||
| 62 | dictionary-connection | ||
| 63 | (eq (connection-status dictionary-connection) 'up) | ||
| 64 | (y-or-n-p | ||
| 65 | (concat "Close existing connection to " dictionary-server "? "))) | ||
| 66 | (connection-close dictionary-connection)) | ||
| 67 | (set-default name value)) | ||
| 68 | |||
| 69 | (defgroup dictionary nil | ||
| 70 | "Client for accessing the dictd server based dictionaries" | ||
| 71 | :group 'hypermedia) | ||
| 72 | |||
| 73 | (defgroup dictionary-proxy nil | ||
| 74 | "Proxy configuration options for the dictionary client" | ||
| 75 | :group 'dictionary) | ||
| 76 | |||
| 77 | (defcustom dictionary-server | ||
| 78 | "dict.org" | ||
| 79 | "This server is contacted for searching the dictionary" | ||
| 80 | :group 'dictionary | ||
| 81 | :set 'dictionary-set-server-var | ||
| 82 | :type 'string) | ||
| 83 | |||
| 84 | (defcustom dictionary-port | ||
| 85 | 2628 | ||
| 86 | "The port of the dictionary server. | ||
| 87 | This port is propably always 2628 so there should be no need to modify it." | ||
| 88 | :group 'dictionary | ||
| 89 | :set 'dictionary-set-server-var | ||
| 90 | :type 'number) | ||
| 91 | |||
| 92 | (defcustom dictionary-identification | ||
| 93 | "dictionary.el emacs lisp dictionary client" | ||
| 94 | "This is the identification string that will be sent to the server." | ||
| 95 | :group 'dictionary | ||
| 96 | :type 'string) | ||
| 97 | |||
| 98 | (defcustom dictionary-default-dictionary | ||
| 99 | "*" | ||
| 100 | "The dictionary which is used for searching definitions and matching. | ||
| 101 | * and ! have a special meaning, * search all dictionaries, ! search until | ||
| 102 | one dictionary yields matches." | ||
| 103 | :group 'dictionary | ||
| 104 | :type 'string) | ||
| 105 | |||
| 106 | (defcustom dictionary-default-strategy | ||
| 107 | "." | ||
| 108 | "The default strategy for listing matching words." | ||
| 109 | :group 'dictionary | ||
| 110 | :type 'string) | ||
| 111 | |||
| 112 | (defcustom dictionary-default-popup-strategy | ||
| 113 | "exact" | ||
| 114 | "The default strategy for listing matching words within a popup window. | ||
| 115 | |||
| 116 | The following algorithm (defined by the dictd server) are supported | ||
| 117 | by the choice value: | ||
| 118 | |||
| 119 | - Exact match | ||
| 120 | |||
| 121 | The found word exactly matches the searched word. | ||
| 122 | |||
| 123 | - Similiar sounding | ||
| 124 | |||
| 125 | The found word sounds similiar to the searched word. For this match type | ||
| 126 | the soundex algorithm defined by Donald E. Knuth is used. It will only | ||
| 127 | works with english words and the algorithm is not very reliable (i.e., | ||
| 128 | the soundex algorithm is quite simple). | ||
| 129 | |||
| 130 | - Levenshtein distance one | ||
| 131 | |||
| 132 | The Levenshtein distance is defined as the number of insertions, deletions, | ||
| 133 | or replacements needed to get the searched word. This algorithm searches | ||
| 134 | for word where spelling mistakes are allowed. Levenshtein distance one | ||
| 135 | means there is either a deleted character, an inserted character, or a | ||
| 136 | modified one. | ||
| 137 | |||
| 138 | - User choice | ||
| 139 | |||
| 140 | Here you can enter any matching algorithm supported by your | ||
| 141 | dictionary server. | ||
| 142 | " | ||
| 143 | :group 'dictionary | ||
| 144 | :type '(choice (const :tag "Exact match" "exact") | ||
| 145 | (const :tag "Similiar sounding" "soundex") | ||
| 146 | (const :tag "Levenshtein distance one" "lev") | ||
| 147 | (string :tag "User choice"))) | ||
| 148 | |||
| 149 | (defcustom dictionary-create-buttons | ||
| 150 | t | ||
| 151 | "Create some clickable buttons on top of the window if non-nil." | ||
| 152 | :group 'dictionary | ||
| 153 | :type 'boolean) | ||
| 154 | |||
| 155 | (defcustom dictionary-mode-hook | ||
| 156 | nil | ||
| 157 | "Hook run in dictionary mode buffers." | ||
| 158 | :group 'dictionary | ||
| 159 | :type 'hook) | ||
| 160 | |||
| 161 | (defcustom dictionary-use-http-proxy | ||
| 162 | nil | ||
| 163 | "Connects via a HTTP proxy using the CONNECT command when not nil." | ||
| 164 | :group 'dictionary-proxy | ||
| 165 | :set 'dictionary-set-server-var | ||
| 166 | :type 'boolean) | ||
| 167 | |||
| 168 | (defcustom dictionary-proxy-server | ||
| 169 | "proxy" | ||
| 170 | "The name of the HTTP proxy to use when dictionary-use-http-proxy is set." | ||
| 171 | :group 'dictionary-proxy | ||
| 172 | :set 'dictionary-set-server-var | ||
| 173 | :type 'string) | ||
| 174 | |||
| 175 | (defcustom dictionary-proxy-port | ||
| 176 | 3128 | ||
| 177 | "The port of the proxy server, used only when dictionary-use-http-proxy is set." | ||
| 178 | :group 'dictionary-proxy | ||
| 179 | :set 'dictionary-set-server-var | ||
| 180 | :type 'number) | ||
| 181 | |||
| 182 | (defcustom dictionary-use-single-buffer | ||
| 183 | nil | ||
| 184 | "Should the dictionary command reuse previous dictionary buffers?" | ||
| 185 | :group 'dictionary | ||
| 186 | :type 'boolean) | ||
| 187 | |||
| 188 | (defcustom dictionary-description-open-delimiter | ||
| 189 | "" | ||
| 190 | "The delimiter to display in front of the dictionaries description" | ||
| 191 | :group 'dictionary | ||
| 192 | :type 'string) | ||
| 193 | |||
| 194 | (defcustom dictionary-description-close-delimiter | ||
| 195 | "" | ||
| 196 | "The delimiter to display after of the dictionaries description" | ||
| 197 | :group 'dictionary | ||
| 198 | :type 'string) | ||
| 199 | |||
| 200 | ;; Define only when coding-system-list is available | ||
| 201 | (when (fboundp 'coding-system-list) | ||
| 202 | (defcustom dictionary-coding-systems-for-dictionaries | ||
| 203 | '( ("mueller" . koi8-r)) | ||
| 204 | "Mapping of dictionaries to coding systems. | ||
| 205 | Each entry in this list defines the coding system to be used for that | ||
| 206 | dictionary. The default coding system for all other dictionaries | ||
| 207 | is utf-8" | ||
| 208 | :group 'dictionary | ||
| 209 | :type `(repeat (cons :tag "Association" | ||
| 210 | (string :tag "Dictionary name") | ||
| 211 | (choice :tag "Coding system" | ||
| 212 | :value 'utf-8 | ||
| 213 | ,@(mapcar (lambda (x) (list 'const x)) | ||
| 214 | (coding-system-list)) | ||
| 215 | )))) | ||
| 216 | |||
| 217 | ) | ||
| 218 | |||
| 219 | (if (fboundp 'defface) | ||
| 220 | (progn | ||
| 221 | |||
| 222 | (defface dictionary-word-definition-face | ||
| 223 | '((((supports (:family "DejaVu Serif"))) | ||
| 224 | (:family "DejaVu Serif")) | ||
| 225 | (((type x)) | ||
| 226 | (:font "Sans Serif")) | ||
| 227 | (t | ||
| 228 | (:font "default"))) | ||
| 229 | "The face that is used for displaying the definition of the word." | ||
| 230 | :group 'dictionary) | ||
| 231 | |||
| 232 | (defface dictionary-word-entry-face | ||
| 233 | '((((type x)) | ||
| 234 | (:italic t)) | ||
| 235 | (((type tty) (class color)) | ||
| 236 | (:foreground "green")) | ||
| 237 | (t | ||
| 238 | (:inverse t))) | ||
| 239 | "The face that is used for displaying the initial word entry line." | ||
| 240 | :group 'dictionary) | ||
| 241 | |||
| 242 | (defface dictionary-button-face | ||
| 243 | '((t | ||
| 244 | (:bold t))) | ||
| 245 | "The face that is used for displaying buttons." | ||
| 246 | :group 'dictionary) | ||
| 247 | |||
| 248 | (defface dictionary-reference-face | ||
| 249 | '((((type x) | ||
| 250 | (class color) | ||
| 251 | (background dark)) | ||
| 252 | (:foreground "yellow")) | ||
| 253 | (((type tty) | ||
| 254 | (class color) | ||
| 255 | (background dark)) | ||
| 256 | (:foreground "cyan")) | ||
| 257 | (((class color) | ||
| 258 | (background light)) | ||
| 259 | (:foreground "blue")) | ||
| 260 | (t | ||
| 261 | (:underline t))) | ||
| 262 | |||
| 263 | "The face that is used for displaying a reference word." | ||
| 264 | :group 'dictionary) | ||
| 265 | |||
| 266 | ) | ||
| 267 | |||
| 268 | ;; else | ||
| 269 | (copy-face 'italic 'dictionary-word-entry-face) | ||
| 270 | (copy-face 'bold 'dictionary-button-face) | ||
| 271 | (copy-face 'default 'dictionary-reference-face) | ||
| 272 | (set-face-foreground 'dictionary-reference-face "blue")) | ||
| 273 | |||
| 274 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 275 | ;; Buffer local variables for storing the current state | ||
| 276 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 277 | |||
| 278 | (defvar dictionary-window-configuration | ||
| 279 | nil | ||
| 280 | "The window configuration to be restored upon closing the buffer") | ||
| 281 | |||
| 282 | (defvar dictionary-selected-window | ||
| 283 | nil | ||
| 284 | "The currently selected window") | ||
| 285 | |||
| 286 | (defvar dictionary-position-stack | ||
| 287 | nil | ||
| 288 | "The history buffer for point and window position") | ||
| 289 | |||
| 290 | (defvar dictionary-data-stack | ||
| 291 | nil | ||
| 292 | "The history buffer for functions and arguments") | ||
| 293 | |||
| 294 | (defvar dictionary-positions | ||
| 295 | nil | ||
| 296 | "The current positions") | ||
| 297 | |||
| 298 | (defvar dictionary-current-data | ||
| 299 | nil | ||
| 300 | "The item that will be placed on stack next time") | ||
| 301 | |||
| 302 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 303 | ;; Global variables | ||
| 304 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 305 | (defvar dictionary-mode-map | ||
| 306 | nil | ||
| 307 | "Keymap for dictionary mode") | ||
| 308 | |||
| 309 | (defvar dictionary-connection | ||
| 310 | nil | ||
| 311 | "The current network connection") | ||
| 312 | |||
| 313 | (defvar dictionary-instances | ||
| 314 | 0 | ||
| 315 | "The number of open dictionary buffers") | ||
| 316 | |||
| 317 | (defvar dictionary-marker | ||
| 318 | nil | ||
| 319 | "Stores the point position while buffer display.") | ||
| 320 | |||
| 321 | (defvar dictionary-color-support | ||
| 322 | (condition-case nil | ||
| 323 | (x-display-color-p) | ||
| 324 | (error nil)) | ||
| 325 | "Determines if the Emacs has support to display color") | ||
| 326 | |||
| 327 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 328 | ;; Basic function providing startup actions | ||
| 329 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 330 | |||
| 331 | ;;;###autoload | ||
| 332 | (defun dictionary-mode () | ||
| 333 | "This is a mode for searching a dictionary server implementing | ||
| 334 | the protocol defined in RFC 2229. | ||
| 335 | |||
| 336 | This is a quick reference to this mode describing the default key bindings: | ||
| 337 | |||
| 338 | * q close the dictionary buffer | ||
| 339 | * h display this help information | ||
| 340 | * s ask for a new word to search | ||
| 341 | * d search the word at point | ||
| 342 | * n or Tab place point to the next link | ||
| 343 | * p or S-Tab place point to the prev link | ||
| 344 | |||
| 345 | * m ask for a pattern and list all matching words. | ||
| 346 | * D select the default dictionary | ||
| 347 | * M select the default search strategy | ||
| 348 | |||
| 349 | * Return or Button2 visit that link | ||
| 350 | * M-Return or M-Button2 search the word beneath link in all dictionaries | ||
| 351 | " | ||
| 352 | |||
| 353 | (unless (eq major-mode 'dictionary-mode) | ||
| 354 | (incf dictionary-instances)) | ||
| 355 | |||
| 356 | (kill-all-local-variables) | ||
| 357 | (buffer-disable-undo) | ||
| 358 | (use-local-map dictionary-mode-map) | ||
| 359 | (setq major-mode 'dictionary-mode) | ||
| 360 | (setq mode-name "Dictionary") | ||
| 361 | |||
| 362 | (make-local-variable 'dictionary-data-stack) | ||
| 363 | (setq dictionary-data-stack nil) | ||
| 364 | (make-local-variable 'dictionary-position-stack) | ||
| 365 | (setq dictionary-position-stack nil) | ||
| 366 | |||
| 367 | (make-local-variable 'dictionary-current-data) | ||
| 368 | (make-local-variable 'dictionary-positions) | ||
| 369 | |||
| 370 | (make-local-variable 'dictionary-default-dictionary) | ||
| 371 | (make-local-variable 'dictionary-default-strategy) | ||
| 372 | |||
| 373 | (if (featurep 'xemacs) | ||
| 374 | (make-local-hook 'kill-buffer-hook)) | ||
| 375 | (add-hook 'kill-buffer-hook 'dictionary-close t t) | ||
| 376 | (run-hooks 'dictionary-mode-hook)) | ||
| 377 | |||
| 378 | ;;;###autoload | ||
| 379 | (defun dictionary () | ||
| 380 | "Create a new dictonary buffer and install dictionary-mode" | ||
| 381 | (interactive) | ||
| 382 | (let ((buffer (or (and dictionary-use-single-buffer | ||
| 383 | (get-buffer "*Dictionary*")) | ||
| 384 | (generate-new-buffer "*Dictionary*"))) | ||
| 385 | (window-configuration (current-window-configuration)) | ||
| 386 | (selected-window (frame-selected-window))) | ||
| 387 | |||
| 388 | (switch-to-buffer-other-window buffer) | ||
| 389 | (dictionary-mode) | ||
| 390 | |||
| 391 | (make-local-variable 'dictionary-window-configuration) | ||
| 392 | (make-local-variable 'dictionary-selected-window) | ||
| 393 | (setq dictionary-window-configuration window-configuration) | ||
| 394 | (setq dictionary-selected-window selected-window) | ||
| 395 | (dictionary-check-connection) | ||
| 396 | (dictionary-new-buffer) | ||
| 397 | (dictionary-store-positions) | ||
| 398 | (dictionary-store-state 'dictionary-new-buffer nil))) | ||
| 399 | |||
| 400 | (defun dictionary-new-buffer (&rest ignore) | ||
| 401 | "Create a new and clean buffer" | ||
| 402 | |||
| 403 | (dictionary-pre-buffer) | ||
| 404 | (dictionary-post-buffer)) | ||
| 405 | |||
| 406 | |||
| 407 | (unless dictionary-mode-map | ||
| 408 | (setq dictionary-mode-map (make-sparse-keymap)) | ||
| 409 | (suppress-keymap dictionary-mode-map) | ||
| 410 | |||
| 411 | (define-key dictionary-mode-map "q" 'dictionary-close) | ||
| 412 | (define-key dictionary-mode-map "h" 'dictionary-help) | ||
| 413 | (define-key dictionary-mode-map "s" 'dictionary-search) | ||
| 414 | (define-key dictionary-mode-map "d" 'dictionary-lookup-definition) | ||
| 415 | (define-key dictionary-mode-map "D" 'dictionary-select-dictionary) | ||
| 416 | (define-key dictionary-mode-map "M" 'dictionary-select-strategy) | ||
| 417 | (define-key dictionary-mode-map "m" 'dictionary-match-words) | ||
| 418 | (define-key dictionary-mode-map "l" 'dictionary-previous) | ||
| 419 | |||
| 420 | (if (and (string-match "GNU" (emacs-version)) | ||
| 421 | (not window-system)) | ||
| 422 | (define-key dictionary-mode-map [9] 'dictionary-next-link) | ||
| 423 | (define-key dictionary-mode-map [tab] 'dictionary-next-link)) | ||
| 424 | |||
| 425 | ;; shift-tabs normally is supported on window systems only, but | ||
| 426 | ;; I do not enforce it | ||
| 427 | (define-key dictionary-mode-map [(shift tab)] 'dictionary-prev-link) | ||
| 428 | (define-key dictionary-mode-map "\e\t" 'dictionary-prev-link) | ||
| 429 | (define-key dictionary-mode-map [backtab] 'dictionary-prev-link) | ||
| 430 | |||
| 431 | (define-key dictionary-mode-map "n" 'dictionary-next-link) | ||
| 432 | (define-key dictionary-mode-map "p" 'dictionary-prev-link) | ||
| 433 | |||
| 434 | (define-key dictionary-mode-map " " 'scroll-up) | ||
| 435 | (define-key dictionary-mode-map [(meta space)] 'scroll-down) | ||
| 436 | |||
| 437 | (link-initialize-keymap dictionary-mode-map)) | ||
| 438 | |||
| 439 | (defmacro dictionary-reply-code (reply) | ||
| 440 | "Return the reply code stored in `reply'." | ||
| 441 | (list 'get reply ''reply-code)) | ||
| 442 | |||
| 443 | (defmacro dictionary-reply (reply) | ||
| 444 | "Return the string reply stored in `reply'." | ||
| 445 | (list 'get reply ''reply)) | ||
| 446 | |||
| 447 | (defmacro dictionary-reply-list (reply) | ||
| 448 | "Return the reply list stored in `reply'." | ||
| 449 | (list 'get reply ''reply-list)) | ||
| 450 | |||
| 451 | (defun dictionary-check-connection () | ||
| 452 | "Check if there is already a connection open" | ||
| 453 | (if (not (and dictionary-connection | ||
| 454 | (eq (connection-status dictionary-connection) 'up))) | ||
| 455 | (let ((wanted 'raw-text) | ||
| 456 | (coding-system nil)) | ||
| 457 | (if (and (fboundp 'coding-system-list) | ||
| 458 | (member wanted (coding-system-list))) | ||
| 459 | (setq coding-system wanted)) | ||
| 460 | (let ((coding-system-for-read coding-system) | ||
| 461 | (coding-system-for-write coding-system)) | ||
| 462 | (message "Opening connection to %s:%s" dictionary-server | ||
| 463 | dictionary-port) | ||
| 464 | (connection-close dictionary-connection) | ||
| 465 | (setq dictionary-connection | ||
| 466 | (if dictionary-use-http-proxy | ||
| 467 | (connection-open dictionary-proxy-server | ||
| 468 | dictionary-proxy-port) | ||
| 469 | (connection-open dictionary-server dictionary-port))) | ||
| 470 | (set-process-query-on-exit-flag | ||
| 471 | (connection-process dictionary-connection) | ||
| 472 | nil) | ||
| 473 | |||
| 474 | (when dictionary-use-http-proxy | ||
| 475 | (message "Proxy CONNECT to %s:%d" | ||
| 476 | dictionary-proxy-server | ||
| 477 | dictionary-proxy-port) | ||
| 478 | (dictionary-send-command (format "CONNECT %s:%d HTTP/1.1" | ||
| 479 | dictionary-server | ||
| 480 | dictionary-port)) | ||
| 481 | ;; just a \r\n combination | ||
| 482 | (dictionary-send-command "") | ||
| 483 | |||
| 484 | ;; read first line of reply | ||
| 485 | (let* ((reply (dictionary-read-reply)) | ||
| 486 | (reply-list (dictionary-split-string reply))) | ||
| 487 | ;; first item is protocol, second item is code | ||
| 488 | (unless (= (string-to-number (cadr reply-list)) 200) | ||
| 489 | (error "Bad reply from proxy server %s" reply)) | ||
| 490 | |||
| 491 | ;; skip the following header lines until empty found | ||
| 492 | (while (not (equal reply "")) | ||
| 493 | (setq reply (dictionary-read-reply))))) | ||
| 494 | |||
| 495 | (dictionary-check-initial-reply) | ||
| 496 | (dictionary-send-command (concat "client " dictionary-identification)) | ||
| 497 | (let ((reply (dictionary-read-reply-and-split))) | ||
| 498 | (message nil) | ||
| 499 | (unless (dictionary-check-reply reply 250) | ||
| 500 | (error "Unknown server answer: %s" | ||
| 501 | (dictionary-reply reply)))))))) | ||
| 502 | |||
| 503 | (defun dictionary-mode-p () | ||
| 504 | "Return non-nil if current buffer has dictionary-mode" | ||
| 505 | (eq major-mode 'dictionary-mode)) | ||
| 506 | |||
| 507 | (defun dictionary-ensure-buffer () | ||
| 508 | "If current buffer is not a dictionary buffer, create a new one." | ||
| 509 | (unless (dictionary-mode-p) | ||
| 510 | (dictionary))) | ||
| 511 | |||
| 512 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 513 | ;; Dealing with closing the buffer | ||
| 514 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 515 | |||
| 516 | (defun dictionary-close (&rest ignore) | ||
| 517 | "Close the current dictionary buffer and its connection" | ||
| 518 | (interactive) | ||
| 519 | (if (eq major-mode 'dictionary-mode) | ||
| 520 | (progn | ||
| 521 | (setq major-mode nil) | ||
| 522 | (if (<= (decf dictionary-instances) 0) | ||
| 523 | (connection-close dictionary-connection)) | ||
| 524 | (let ((configuration dictionary-window-configuration) | ||
| 525 | (selected-window dictionary-selected-window)) | ||
| 526 | (kill-buffer (current-buffer)) | ||
| 527 | (set-window-configuration configuration) | ||
| 528 | (select-window selected-window))))) | ||
| 529 | |||
| 530 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 531 | ;; Helpful functions | ||
| 532 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 533 | |||
| 534 | (defun dictionary-send-command (string) | ||
| 535 | "Send the command `string' to the network connection." | ||
| 536 | (dictionary-check-connection) | ||
| 537 | ;;;; ##### | ||
| 538 | (connection-send-crlf dictionary-connection string)) | ||
| 539 | |||
| 540 | (defun dictionary-read-reply () | ||
| 541 | "Read the reply line from the server" | ||
| 542 | (let ((answer (connection-read-crlf dictionary-connection))) | ||
| 543 | (if (string-match "\r?\n" answer) | ||
| 544 | (substring answer 0 (match-beginning 0)) | ||
| 545 | answer))) | ||
| 546 | |||
| 547 | (defun dictionary-split-string (string) | ||
| 548 | "Split the `string' constiting of space separated words into elements. | ||
| 549 | This function knows about the special meaning of quotes (\")" | ||
| 550 | (let ((list)) | ||
| 551 | (while (and string (> (length string) 0)) | ||
| 552 | (let ((search "\\(\\s-+\\)") | ||
| 553 | (start 0)) | ||
| 554 | (if (= (aref string 0) ?\") | ||
| 555 | (setq search "\\(\"\\)\\s-*" | ||
| 556 | start 1)) | ||
| 557 | (if (string-match search string start) | ||
| 558 | (progn | ||
| 559 | (setq list (cons (substring string start (- (match-end 1) 1)) list) | ||
| 560 | string (substring string (match-end 0)))) | ||
| 561 | (setq list (cons string list) | ||
| 562 | string nil)))) | ||
| 563 | (nreverse list))) | ||
| 564 | |||
| 565 | (defun dictionary-read-reply-and-split () | ||
| 566 | "Read the reply, split it into words and return it" | ||
| 567 | (let ((answer (make-symbol "reply-data")) | ||
| 568 | (reply (dictionary-read-reply))) | ||
| 569 | (let ((reply-list (dictionary-split-string reply))) | ||
| 570 | (put answer 'reply reply) | ||
| 571 | (put answer 'reply-list reply-list) | ||
| 572 | (put answer 'reply-code (string-to-number (car reply-list))) | ||
| 573 | answer))) | ||
| 574 | |||
| 575 | (defun dictionary-read-answer () | ||
| 576 | "Read an answer delimited by a . on a single line" | ||
| 577 | (let ((answer (connection-read-to-point dictionary-connection)) | ||
| 578 | (start 0)) | ||
| 579 | (while (string-match "\r\n" answer start) | ||
| 580 | (setq answer (replace-match "\n" t t answer)) | ||
| 581 | (setq start (1- (match-end 0)))) | ||
| 582 | (setq start 0) | ||
| 583 | (if (string-match "\n\\.\n.*" answer start) | ||
| 584 | (setq answer (replace-match "" t t answer))) | ||
| 585 | answer)) | ||
| 586 | |||
| 587 | (defun dictionary-check-reply (reply code) | ||
| 588 | "Check if the reply in `reply' has the `code'." | ||
| 589 | (let ((number (dictionary-reply-code reply))) | ||
| 590 | (and (numberp number) | ||
| 591 | (= number code)))) | ||
| 592 | |||
| 593 | (defun dictionary-coding-system (dictionary) | ||
| 594 | "Select coding system to use for that dictionary" | ||
| 595 | (when (boundp 'dictionary-coding-systems-for-dictionaries) | ||
| 596 | (let ((coding-system | ||
| 597 | (or (cdr (assoc dictionary | ||
| 598 | dictionary-coding-systems-for-dictionaries)) | ||
| 599 | 'utf-8))) | ||
| 600 | (if (member coding-system (coding-system-list)) | ||
| 601 | coding-system | ||
| 602 | nil)))) | ||
| 603 | |||
| 604 | (defun dictionary-decode-charset (text dictionary) | ||
| 605 | "Convert the text from the charset defined by the dictionary given." | ||
| 606 | (let ((coding-system (dictionary-coding-system dictionary))) | ||
| 607 | (if coding-system | ||
| 608 | (decode-coding-string text coding-system) | ||
| 609 | text))) | ||
| 610 | |||
| 611 | (defun dictionary-encode-charset (text dictionary) | ||
| 612 | "Convert the text to the charset defined by the dictionary given." | ||
| 613 | (let ((coding-system (dictionary-coding-system dictionary))) | ||
| 614 | (if coding-system | ||
| 615 | (encode-coding-string text coding-system) | ||
| 616 | text))) | ||
| 617 | |||
| 618 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 619 | ;; Communication functions | ||
| 620 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 621 | |||
| 622 | (defun dictionary-check-initial-reply () | ||
| 623 | "Read the first reply from server and check it." | ||
| 624 | (let ((reply (dictionary-read-reply-and-split))) | ||
| 625 | (unless (dictionary-check-reply reply 220) | ||
| 626 | (connection-close dictionary-connection) | ||
| 627 | (error "Server returned: %s" (dictionary-reply reply))))) | ||
| 628 | |||
| 629 | ;; Store the current state | ||
| 630 | (defun dictionary-store-state (function data) | ||
| 631 | "Stores the current state of operation for later restore." | ||
| 632 | |||
| 633 | (if dictionary-current-data | ||
| 634 | (progn | ||
| 635 | (push dictionary-current-data dictionary-data-stack) | ||
| 636 | (unless dictionary-positions | ||
| 637 | (error "dictionary-store-state called before dictionary-store-positions")) | ||
| 638 | (push dictionary-positions dictionary-position-stack))) | ||
| 639 | (setq dictionary-current-data | ||
| 640 | (cons function data))) | ||
| 641 | |||
| 642 | (defun dictionary-store-positions () | ||
| 643 | "Stores the current positions for later restore." | ||
| 644 | |||
| 645 | (setq dictionary-positions (cons (point) (window-start)))) | ||
| 646 | |||
| 647 | ;; Restore the previous state | ||
| 648 | (defun dictionary-restore-state (&rest ignored) | ||
| 649 | "Restore the state just before the last operation" | ||
| 650 | (let ((position (pop dictionary-position-stack)) | ||
| 651 | (data (pop dictionary-data-stack))) | ||
| 652 | (unless position | ||
| 653 | (error "Already at begin of history")) | ||
| 654 | (apply (car data) (cdr data)) | ||
| 655 | (set-window-start (selected-window) (cdr position)) | ||
| 656 | (goto-char (car position)) | ||
| 657 | (setq dictionary-current-data data))) | ||
| 658 | |||
| 659 | ;; The normal search | ||
| 660 | |||
| 661 | (defun dictionary-new-search (args &optional all) | ||
| 662 | "Save the current state and start a new search" | ||
| 663 | (interactive) | ||
| 664 | (dictionary-store-positions) | ||
| 665 | (let ((word (car args)) | ||
| 666 | (dictionary (cdr args))) | ||
| 667 | |||
| 668 | (if all | ||
| 669 | (setq dictionary dictionary-default-dictionary)) | ||
| 670 | (dictionary-ensure-buffer) | ||
| 671 | (dictionary-new-search-internal word dictionary 'dictionary-display-search-result) | ||
| 672 | (dictionary-store-state 'dictionary-new-search-internal | ||
| 673 | (list word dictionary 'dictionary-display-search-result)))) | ||
| 674 | |||
| 675 | (defun dictionary-new-search-internal (word dictionary function) | ||
| 676 | "Starts a new search after preparing the buffer" | ||
| 677 | (dictionary-pre-buffer) | ||
| 678 | (dictionary-do-search word dictionary function)) | ||
| 679 | |||
| 680 | (defun dictionary-do-search (word dictionary function &optional nomatching) | ||
| 681 | "The workhorse for doing the search" | ||
| 682 | |||
| 683 | (message "Searching for %s in %s" word dictionary) | ||
| 684 | (dictionary-send-command (concat "define " | ||
| 685 | (dictionary-encode-charset dictionary "") | ||
| 686 | " \"" | ||
| 687 | (dictionary-encode-charset word dictionary) | ||
| 688 | "\"")) | ||
| 689 | |||
| 690 | (message nil) | ||
| 691 | (let ((reply (dictionary-read-reply-and-split))) | ||
| 692 | (if (dictionary-check-reply reply 552) | ||
| 693 | (progn | ||
| 694 | (unless nomatching | ||
| 695 | (beep) | ||
| 696 | (insert "Word not found, maybe you are looking " | ||
| 697 | "for one of these words\n\n") | ||
| 698 | (dictionary-do-matching word | ||
| 699 | dictionary | ||
| 700 | "." | ||
| 701 | 'dictionary-display-only-match-result) | ||
| 702 | (dictionary-post-buffer))) | ||
| 703 | (if (dictionary-check-reply reply 550) | ||
| 704 | (error "Dictionary \"%s\" is unknown, please select an existing one." | ||
| 705 | dictionary) | ||
| 706 | (unless (dictionary-check-reply reply 150) | ||
| 707 | (error "Unknown server answer: %s" (dictionary-reply reply))) | ||
| 708 | (funcall function reply))))) | ||
| 709 | |||
| 710 | (defun dictionary-pre-buffer () | ||
| 711 | "These commands are executed at the begin of a new buffer" | ||
| 712 | (setq buffer-read-only nil) | ||
| 713 | (erase-buffer) | ||
| 714 | (if dictionary-create-buttons | ||
| 715 | (progn | ||
| 716 | (link-insert-link "[Back]" 'dictionary-button-face | ||
| 717 | 'dictionary-restore-state nil | ||
| 718 | "Mouse-2 to go backwards in history") | ||
| 719 | (insert " ") | ||
| 720 | (link-insert-link "[Search Definition]" | ||
| 721 | 'dictionary-button-face | ||
| 722 | 'dictionary-search nil | ||
| 723 | "Mouse-2 to look up a new word") | ||
| 724 | (insert " ") | ||
| 725 | |||
| 726 | (link-insert-link "[Matching words]" | ||
| 727 | 'dictionary-button-face | ||
| 728 | 'dictionary-match-words nil | ||
| 729 | "Mouse-2 to find matches for a pattern") | ||
| 730 | (insert " ") | ||
| 731 | |||
| 732 | (link-insert-link "[Quit]" 'dictionary-button-face | ||
| 733 | 'dictionary-close nil | ||
| 734 | "Mouse-2 to close this window") | ||
| 735 | |||
| 736 | (insert "\n ") | ||
| 737 | |||
| 738 | (link-insert-link "[Select Dictionary]" | ||
| 739 | 'dictionary-button-face | ||
| 740 | 'dictionary-select-dictionary nil | ||
| 741 | "Mouse-2 to select dictionary for future searches") | ||
| 742 | (insert " ") | ||
| 743 | (link-insert-link "[Select Match Strategy]" | ||
| 744 | 'dictionary-button-face | ||
| 745 | 'dictionary-select-strategy nil | ||
| 746 | "Mouse-2 to select matching algorithm") | ||
| 747 | (insert "\n\n"))) | ||
| 748 | (setq dictionary-marker (point-marker))) | ||
| 749 | |||
| 750 | (defun dictionary-post-buffer () | ||
| 751 | "These commands are executed at the end of a new buffer" | ||
| 752 | (goto-char dictionary-marker) | ||
| 753 | |||
| 754 | (set-buffer-modified-p nil) | ||
| 755 | (setq buffer-read-only t)) | ||
| 756 | |||
| 757 | (defun dictionary-display-search-result (reply) | ||
| 758 | "This function starts displaying the result starting with the `reply'." | ||
| 759 | |||
| 760 | (let ((number (nth 1 (dictionary-reply-list reply)))) | ||
| 761 | (insert number (if (equal number "1") | ||
| 762 | " definition" | ||
| 763 | " definitions") | ||
| 764 | " found\n\n") | ||
| 765 | (setq reply (dictionary-read-reply-and-split)) | ||
| 766 | (while (dictionary-check-reply reply 151) | ||
| 767 | (let* ((reply-list (dictionary-reply-list reply)) | ||
| 768 | (dictionary (nth 2 reply-list)) | ||
| 769 | (description (nth 3 reply-list)) | ||
| 770 | (word (nth 1 reply-list))) | ||
| 771 | (dictionary-display-word-entry word dictionary description) | ||
| 772 | (setq reply (dictionary-read-answer)) | ||
| 773 | (dictionary-display-word-definition reply word dictionary) | ||
| 774 | (setq reply (dictionary-read-reply-and-split)))) | ||
| 775 | (dictionary-post-buffer))) | ||
| 776 | |||
| 777 | (defun dictionary-display-word-entry (word dictionary description) | ||
| 778 | "Insert an explanation for the current definition." | ||
| 779 | (let ((start (point))) | ||
| 780 | (insert "From " | ||
| 781 | dictionary-description-open-delimiter | ||
| 782 | (dictionary-decode-charset description dictionary) | ||
| 783 | dictionary-description-close-delimiter | ||
| 784 | " [" (dictionary-decode-charset dictionary dictionary) "]:" | ||
| 785 | "\n\n") | ||
| 786 | (put-text-property start (point) 'face 'dictionary-word-entry-face))) | ||
| 787 | |||
| 788 | (defun dictionary-display-word-definition (reply word dictionary) | ||
| 789 | "Insert the definition for the current word" | ||
| 790 | (let ((start (point))) | ||
| 791 | (insert (dictionary-decode-charset reply dictionary)) | ||
| 792 | (insert "\n\n") | ||
| 793 | (put-text-property start (point) 'face 'dictionary-word-definition-face) | ||
| 794 | (let ((regexp "\\({+\\)\\([^ '\"][^}]*\\)\\(}+\\)")) | ||
| 795 | (goto-char start) | ||
| 796 | (while (< (point) (point-max)) | ||
| 797 | (if (search-forward-regexp regexp nil t) | ||
| 798 | (let ((match-start (match-beginning 2)) | ||
| 799 | (match-end (match-end 2))) | ||
| 800 | (if dictionary-color-support | ||
| 801 | ;; Compensate for the replacement | ||
| 802 | (let ((brace-match-length (- (match-end 1) | ||
| 803 | (match-beginning 1)))) | ||
| 804 | (setq match-start (- (match-beginning 2) | ||
| 805 | brace-match-length)) | ||
| 806 | (setq match-end (- (match-end 2) | ||
| 807 | brace-match-length)) | ||
| 808 | (replace-match "\\2"))) | ||
| 809 | (dictionary-mark-reference match-start match-end | ||
| 810 | 'dictionary-new-search | ||
| 811 | word dictionary)) | ||
| 812 | (goto-char (point-max))))))) | ||
| 813 | |||
| 814 | (defun dictionary-mark-reference (start end call displayed-word dictionary) | ||
| 815 | "Format the area from `start' to `end' as link calling `call'. | ||
| 816 | The word is taken from the buffer, the `dictionary' is given as argument." | ||
| 817 | (let ((word (buffer-substring-no-properties start end))) | ||
| 818 | (while (string-match "\n\\s-*" word) | ||
| 819 | (setq word (replace-match " " t t word))) | ||
| 820 | (while (string-match "[*\"]" word) | ||
| 821 | (setq word (replace-match "" t t word))) | ||
| 822 | |||
| 823 | (unless (equal word displayed-word) | ||
| 824 | (link-create-link start end 'dictionary-reference-face | ||
| 825 | call (cons word dictionary) | ||
| 826 | (concat "Press Mouse-2 to lookup \"" | ||
| 827 | word "\" in \"" dictionary "\""))))) | ||
| 828 | |||
| 829 | (defun dictionary-select-dictionary (&rest ignored) | ||
| 830 | "Save the current state and start a dictionary selection" | ||
| 831 | (interactive) | ||
| 832 | (dictionary-ensure-buffer) | ||
| 833 | (dictionary-store-positions) | ||
| 834 | (dictionary-do-select-dictionary) | ||
| 835 | (dictionary-store-state 'dictionary-do-select-dictionary nil)) | ||
| 836 | |||
| 837 | (defun dictionary-do-select-dictionary (&rest ignored) | ||
| 838 | "The workhorse for doing the dictionary selection." | ||
| 839 | |||
| 840 | (message "Looking up databases and descriptions") | ||
| 841 | (dictionary-send-command "show db") | ||
| 842 | |||
| 843 | (let ((reply (dictionary-read-reply-and-split))) | ||
| 844 | (message nil) | ||
| 845 | (if (dictionary-check-reply reply 554) | ||
| 846 | (error "No dictionary present") | ||
| 847 | (unless (dictionary-check-reply reply 110) | ||
| 848 | (error "Unknown server answer: %s" | ||
| 849 | (dictionary-reply reply))) | ||
| 850 | (dictionary-display-dictionarys reply)))) | ||
| 851 | |||
| 852 | (defun dictionary-simple-split-string (string &optional pattern) | ||
| 853 | "Return a list of substrings of STRING which are separated by PATTERN. | ||
| 854 | If PATTERN is omitted, it defaults to \"[ \\f\\t\\n\\r\\v]+\"." | ||
| 855 | (or pattern | ||
| 856 | (setq pattern "[ \f\t\n\r\v]+")) | ||
| 857 | ;; The FSF version of this function takes care not to cons in case | ||
| 858 | ;; of infloop. Maybe we should synch? | ||
| 859 | (let (parts (start 0)) | ||
| 860 | (while (string-match pattern string start) | ||
| 861 | (setq parts (cons (substring string start (match-beginning 0)) parts) | ||
| 862 | start (match-end 0))) | ||
| 863 | (nreverse (cons (substring string start) parts)))) | ||
| 864 | |||
| 865 | (defun dictionary-display-dictionarys (reply) | ||
| 866 | "Handle the display of all dictionaries existing on the server" | ||
| 867 | (dictionary-pre-buffer) | ||
| 868 | (insert "Please select your default dictionary:\n\n") | ||
| 869 | (dictionary-display-dictionary-line "* \"All dictionaries\"") | ||
| 870 | (dictionary-display-dictionary-line "! \"The first matching dictionary\"") | ||
| 871 | (let* ((reply (dictionary-read-answer)) | ||
| 872 | (list (dictionary-simple-split-string reply "\n+"))) | ||
| 873 | (mapc 'dictionary-display-dictionary-line list)) | ||
| 874 | (dictionary-post-buffer)) | ||
| 875 | |||
| 876 | (defun dictionary-display-dictionary-line (string) | ||
| 877 | "Display a single dictionary" | ||
| 878 | (let* ((list (dictionary-split-string string)) | ||
| 879 | (dictionary (car list)) | ||
| 880 | (description (cadr list)) | ||
| 881 | (translated (dictionary-decode-charset description dictionary))) | ||
| 882 | (if dictionary | ||
| 883 | (if (equal dictionary "--exit--") | ||
| 884 | (insert "(end of default search list)\n") | ||
| 885 | (link-insert-link (concat dictionary ": " translated) | ||
| 886 | 'dictionary-reference-face | ||
| 887 | 'dictionary-set-dictionary | ||
| 888 | (cons dictionary description) | ||
| 889 | "Mouse-2 to select this dictionary") | ||
| 890 | (insert "\n"))))) | ||
| 891 | |||
| 892 | (defun dictionary-set-dictionary (param &optional more) | ||
| 893 | "Select this dictionary as new default" | ||
| 894 | |||
| 895 | (if more | ||
| 896 | (dictionary-display-more-info param) | ||
| 897 | (let ((dictionary (car param))) | ||
| 898 | (setq dictionary-default-dictionary dictionary) | ||
| 899 | (dictionary-restore-state) | ||
| 900 | (message "Dictionary %s has been selected" dictionary)))) | ||
| 901 | |||
| 902 | (defun dictionary-display-more-info (param) | ||
| 903 | "Display the available information on the dictionary" | ||
| 904 | |||
| 905 | (let ((dictionary (car param)) | ||
| 906 | (description (cdr param))) | ||
| 907 | (unless (or (equal dictionary "*") | ||
| 908 | (equal dictionary "!")) | ||
| 909 | (dictionary-store-positions) | ||
| 910 | (message "Requesting more information on %s" dictionary) | ||
| 911 | (dictionary-send-command | ||
| 912 | (concat "show info " (dictionary-encode-charset dictionary ""))) | ||
| 913 | (let ((reply (dictionary-read-reply-and-split))) | ||
| 914 | (message nil) | ||
| 915 | (if (dictionary-check-reply reply 550) | ||
| 916 | (error "Dictionary \"%s\" not existing" dictionary) | ||
| 917 | (unless (dictionary-check-reply reply 112) | ||
| 918 | (error "Unknown server answer: %s" (dictionary-reply reply))) | ||
| 919 | (dictionary-pre-buffer) | ||
| 920 | (insert "Information on dictionary: ") | ||
| 921 | (link-insert-link description 'dictionary-reference-face | ||
| 922 | 'dictionary-set-dictionary | ||
| 923 | (cons dictionary description) | ||
| 924 | "Mouse-2 to select this dictionary") | ||
| 925 | (insert "\n\n") | ||
| 926 | (setq reply (dictionary-read-answer)) | ||
| 927 | (insert reply) | ||
| 928 | (dictionary-post-buffer))) | ||
| 929 | |||
| 930 | (dictionary-store-state 'dictionary-display-more-info dictionary)))) | ||
| 931 | |||
| 932 | (defun dictionary-select-strategy (&rest ignored) | ||
| 933 | "Save the current state and start a strategy selection" | ||
| 934 | (interactive) | ||
| 935 | (dictionary-ensure-buffer) | ||
| 936 | (dictionary-store-positions) | ||
| 937 | (dictionary-do-select-strategy) | ||
| 938 | (dictionary-store-state 'dictionary-do-select-strategy nil)) | ||
| 939 | |||
| 940 | (defun dictionary-do-select-strategy () | ||
| 941 | "The workhorse for doing the strategy selection." | ||
| 942 | |||
| 943 | (message "Request existing matching algorithm") | ||
| 944 | (dictionary-send-command "show strat") | ||
| 945 | |||
| 946 | (let ((reply (dictionary-read-reply-and-split))) | ||
| 947 | (message nil) | ||
| 948 | (if (dictionary-check-reply reply 555) | ||
| 949 | (error "No strategies available") | ||
| 950 | (unless (dictionary-check-reply reply 111) | ||
| 951 | (error "Unknown server answer: %s" | ||
| 952 | (dictionary-reply reply))) | ||
| 953 | (dictionary-display-strategies reply)))) | ||
| 954 | |||
| 955 | (defun dictionary-display-strategies (reply) | ||
| 956 | "Handle the display of all strategies existing on the server" | ||
| 957 | (dictionary-pre-buffer) | ||
| 958 | (insert "Please select your default search strategy:\n\n") | ||
| 959 | (dictionary-display-strategy-line ". \"The servers default\"") | ||
| 960 | (let* ((reply (dictionary-read-answer)) | ||
| 961 | (list (dictionary-simple-split-string reply "\n+"))) | ||
| 962 | (mapc 'dictionary-display-strategy-line list)) | ||
| 963 | (dictionary-post-buffer)) | ||
| 964 | |||
| 965 | (defun dictionary-display-strategy-line (string) | ||
| 966 | "Display a single strategy" | ||
| 967 | (let* ((list (dictionary-split-string string)) | ||
| 968 | (strategy (car list)) | ||
| 969 | (description (cadr list))) | ||
| 970 | (if strategy | ||
| 971 | (progn | ||
| 972 | (link-insert-link description 'dictionary-reference-face | ||
| 973 | 'dictionary-set-strategy strategy | ||
| 974 | "Mouse-2 to select this matching algorithm") | ||
| 975 | (insert "\n"))))) | ||
| 976 | |||
| 977 | (defun dictionary-set-strategy (strategy &rest ignored) | ||
| 978 | "Select this strategy as new default" | ||
| 979 | (setq dictionary-default-strategy strategy) | ||
| 980 | (dictionary-restore-state) | ||
| 981 | (message "Strategy %s has been selected" strategy)) | ||
| 982 | |||
| 983 | (defun dictionary-new-matching (word) | ||
| 984 | "Run a new matching search on `word'." | ||
| 985 | (dictionary-ensure-buffer) | ||
| 986 | (dictionary-store-positions) | ||
| 987 | (dictionary-do-matching word dictionary-default-dictionary | ||
| 988 | dictionary-default-strategy | ||
| 989 | 'dictionary-display-match-result) | ||
| 990 | (dictionary-store-state 'dictionary-do-matching | ||
| 991 | (list word dictionary-default-dictionary | ||
| 992 | dictionary-default-strategy | ||
| 993 | 'dictionary-display-match-result))) | ||
| 994 | |||
| 995 | (defun dictionary-do-matching (word dictionary strategy function) | ||
| 996 | "Ask the server about matches to `word' and display it." | ||
| 997 | |||
| 998 | (message "Lookup matching words for %s in %s using %s" | ||
| 999 | word dictionary strategy) | ||
| 1000 | (dictionary-send-command | ||
| 1001 | (concat "match " (dictionary-encode-charset dictionary "") " " | ||
| 1002 | (dictionary-encode-charset strategy "") " \"" | ||
| 1003 | (dictionary-encode-charset word "") "\"")) | ||
| 1004 | (let ((reply (dictionary-read-reply-and-split))) | ||
| 1005 | (message nil) | ||
| 1006 | (if (dictionary-check-reply reply 550) | ||
| 1007 | (error "Dictionary \"%s\" is invalid" dictionary)) | ||
| 1008 | (if (dictionary-check-reply reply 551) | ||
| 1009 | (error "Strategy \"%s\" is invalid" strategy)) | ||
| 1010 | (if (dictionary-check-reply reply 552) | ||
| 1011 | (error (concat | ||
| 1012 | "No match for \"%s\" with strategy \"%s\" in " | ||
| 1013 | "dictionary \"%s\".") | ||
| 1014 | word strategy dictionary)) | ||
| 1015 | (unless (dictionary-check-reply reply 152) | ||
| 1016 | (error "Unknown server answer: %s" (dictionary-reply reply))) | ||
| 1017 | (funcall function reply))) | ||
| 1018 | |||
| 1019 | (defun dictionary-display-only-match-result (reply) | ||
| 1020 | "Display the results from the current matches without the headers." | ||
| 1021 | |||
| 1022 | (let ((number (nth 1 (dictionary-reply-list reply))) | ||
| 1023 | (list (dictionary-simple-split-string (dictionary-read-answer) "\n+"))) | ||
| 1024 | (insert number " matching word" (if (equal number "1") "" "s") | ||
| 1025 | " found\n\n") | ||
| 1026 | (let ((result nil)) | ||
| 1027 | (mapc (lambda (item) | ||
| 1028 | (let* ((list (dictionary-split-string item)) | ||
| 1029 | (dictionary (car list)) | ||
| 1030 | (word (cadr list)) | ||
| 1031 | (hash (assoc dictionary result))) | ||
| 1032 | (if dictionary | ||
| 1033 | (if hash | ||
| 1034 | (setcdr hash (cons word (cdr hash))) | ||
| 1035 | (setq result (cons | ||
| 1036 | (cons dictionary (list word)) | ||
| 1037 | result)))))) | ||
| 1038 | list) | ||
| 1039 | (dictionary-display-match-lines (reverse result))))) | ||
| 1040 | |||
| 1041 | (defun dictionary-display-match-result (reply) | ||
| 1042 | "Display the results from the current matches." | ||
| 1043 | (dictionary-pre-buffer) | ||
| 1044 | |||
| 1045 | (let ((number (nth 1 (dictionary-reply-list reply))) | ||
| 1046 | (list (dictionary-simple-split-string (dictionary-read-answer) "\n+"))) | ||
| 1047 | (insert number " matching word" (if (equal number "1") "" "s") | ||
| 1048 | " found\n\n") | ||
| 1049 | (let ((result nil)) | ||
| 1050 | (mapc (lambda (item) | ||
| 1051 | (let* ((list (dictionary-split-string item)) | ||
| 1052 | (dictionary (car list)) | ||
| 1053 | (word (cadr list)) | ||
| 1054 | (hash (assoc dictionary result))) | ||
| 1055 | (if dictionary | ||
| 1056 | (if hash | ||
| 1057 | (setcdr hash (cons word (cdr hash))) | ||
| 1058 | (setq result (cons | ||
| 1059 | (cons dictionary (list word)) | ||
| 1060 | result)))))) | ||
| 1061 | list) | ||
| 1062 | (dictionary-display-match-lines (reverse result)))) | ||
| 1063 | (dictionary-post-buffer)) | ||
| 1064 | |||
| 1065 | (defun dictionary-display-match-lines (list) | ||
| 1066 | "Display the match lines." | ||
| 1067 | (mapc (lambda (item) | ||
| 1068 | (let ((dictionary (car item)) | ||
| 1069 | (word-list (cdr item))) | ||
| 1070 | (insert "Matches from " dictionary ":\n") | ||
| 1071 | (mapc (lambda (word) | ||
| 1072 | (setq word (dictionary-decode-charset word dictionary)) | ||
| 1073 | (insert " ") | ||
| 1074 | (link-insert-link word | ||
| 1075 | 'dictionary-reference-face | ||
| 1076 | 'dictionary-new-search | ||
| 1077 | (cons word dictionary) | ||
| 1078 | "Mouse-2 to lookup word") | ||
| 1079 | (insert "\n")) (reverse word-list)) | ||
| 1080 | (insert "\n"))) | ||
| 1081 | list)) | ||
| 1082 | |||
| 1083 | ;; Returns a sensible default for dictionary-search: | ||
| 1084 | ;; - if region is active returns its contents | ||
| 1085 | ;; - otherwise return the word near the point | ||
| 1086 | (defun dictionary-search-default () | ||
| 1087 | (if (use-region-p) | ||
| 1088 | (buffer-substring-no-properties (region-beginning) (region-end)) | ||
| 1089 | (current-word t))) | ||
| 1090 | |||
| 1091 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 1092 | ;; User callable commands | ||
| 1093 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| 1094 | |||
| 1095 | ;;;###autoload | ||
| 1096 | (defun dictionary-search (word &optional dictionary) | ||
| 1097 | "Search the `word' in `dictionary' if given or in all if nil. | ||
| 1098 | It presents the word at point as default input and allows editing it." | ||
| 1099 | (interactive | ||
| 1100 | (list (let ((default (dictionary-search-default))) | ||
| 1101 | (read-string (if default | ||
| 1102 | (format "Search word (%s): " default) | ||
| 1103 | "Search word: ") | ||
| 1104 | nil nil default)) | ||
| 1105 | (if current-prefix-arg | ||
| 1106 | (read-string (if dictionary-default-dictionary | ||
| 1107 | (format "Dictionary (%s): " dictionary-default-dictionary) | ||
| 1108 | "Dictionary: ") | ||
| 1109 | nil nil dictionary-default-dictionary) | ||
| 1110 | dictionary-default-dictionary))) | ||
| 1111 | |||
| 1112 | ;; if called by pressing the button | ||
| 1113 | (unless word | ||
| 1114 | (setq word (read-string "Search word: "))) | ||
| 1115 | ;; just in case non-interactivly called | ||
| 1116 | (unless dictionary | ||
| 1117 | (setq dictionary dictionary-default-dictionary)) | ||
| 1118 | (dictionary-new-search (cons word dictionary))) | ||
| 1119 | |||
| 1120 | ;;;###autoload | ||
| 1121 | (defun dictionary-lookup-definition () | ||
| 1122 | "Unconditionally lookup the word at point." | ||
| 1123 | (interactive) | ||
| 1124 | (dictionary-new-search (cons (current-word) dictionary-default-dictionary))) | ||
| 1125 | |||
| 1126 | (defun dictionary-previous () | ||
| 1127 | "Go to the previous location in the current buffer" | ||
| 1128 | (interactive) | ||
| 1129 | (unless (dictionary-mode-p) | ||
| 1130 | (error "Current buffer is no dictionary buffer")) | ||
| 1131 | (dictionary-restore-state)) | ||
| 1132 | |||
| 1133 | (defun dictionary-next-link () | ||
| 1134 | "Place the cursor to the next link." | ||
| 1135 | (interactive) | ||
| 1136 | (let ((pos (link-next-link))) | ||
| 1137 | (if pos | ||
| 1138 | (goto-char pos) | ||
| 1139 | (error "There is no next link")))) | ||
| 1140 | |||
| 1141 | (defun dictionary-prev-link () | ||
| 1142 | "Place the cursor to the previous link." | ||
| 1143 | (interactive) | ||
| 1144 | (let ((pos (link-prev-link))) | ||
| 1145 | (if pos | ||
| 1146 | (goto-char pos) | ||
| 1147 | (error "There is no previous link")))) | ||
| 1148 | |||
| 1149 | (defun dictionary-help () | ||
| 1150 | "Display a little help" | ||
| 1151 | (interactive) | ||
| 1152 | (describe-function 'dictionary-mode)) | ||
| 1153 | |||
| 1154 | ;;;###autoload | ||
| 1155 | (defun dictionary-match-words (&optional pattern &rest ignored) | ||
| 1156 | "Search `pattern' in current default dictionary using default strategy." | ||
| 1157 | (interactive) | ||
| 1158 | ;; can't use interactive because of mouse events | ||
| 1159 | (or pattern | ||
| 1160 | (setq pattern (read-string "Search pattern: "))) | ||
| 1161 | (dictionary-new-matching pattern)) | ||
| 1162 | |||
| 1163 | ;;;###autoload | ||
| 1164 | (defun dictionary-mouse-popup-matching-words (event) | ||
| 1165 | "Display entries matching the word at the cursor" | ||
| 1166 | (interactive "e") | ||
| 1167 | (let ((word (save-window-excursion | ||
| 1168 | (save-excursion | ||
| 1169 | (mouse-set-point event) | ||
| 1170 | (current-word))))) | ||
| 1171 | (selected-window) | ||
| 1172 | (dictionary-popup-matching-words word))) | ||
| 1173 | |||
| 1174 | ;;;###autoload | ||
| 1175 | (defun dictionary-popup-matching-words (&optional word) | ||
| 1176 | "Display entries matching the word at the point" | ||
| 1177 | (interactive) | ||
| 1178 | (unless (functionp 'popup-menu) | ||
| 1179 | (error "Sorry, popup menus are not available in this emacs version")) | ||
| 1180 | (dictionary-do-matching (or word (current-word)) | ||
| 1181 | dictionary-default-dictionary | ||
| 1182 | dictionary-default-popup-strategy | ||
| 1183 | 'dictionary-process-popup-replies)) | ||
| 1184 | |||
| 1185 | (defun dictionary-process-popup-replies (reply) | ||
| 1186 | (let ((number (nth 1 (dictionary-reply-list reply))) | ||
| 1187 | (list (dictionary-simple-split-string (dictionary-read-answer) "\n+"))) | ||
| 1188 | |||
| 1189 | (let ((result (mapcar (lambda (item) | ||
| 1190 | (let* ((list (dictionary-split-string item)) | ||
| 1191 | (dictionary (car list)) | ||
| 1192 | (word (dictionary-decode-charset | ||
| 1193 | (cadr list) dictionary))) | ||
| 1194 | (message word) | ||
| 1195 | (if (equal word "") | ||
| 1196 | [ "-" nil nil] | ||
| 1197 | (vector (concat "[" dictionary "] " word) | ||
| 1198 | `(dictionary-new-search | ||
| 1199 | '(,word . ,dictionary)) | ||
| 1200 | t )))) | ||
| 1201 | |||
| 1202 | list))) | ||
| 1203 | (let ((menu (make-sparse-keymap 'dictionary-popup))) | ||
| 1204 | |||
| 1205 | (easy-menu-define dictionary-mode-map-menu dictionary-mode-map | ||
| 1206 | "Menu used for displaying dictionary popup" | ||
| 1207 | (cons "Matching words" | ||
| 1208 | `(,@result))) | ||
| 1209 | (popup-menu dictionary-mode-map-menu))))) | ||
| 1210 | |||
| 1211 | ;;; Tooltip support | ||
| 1212 | |||
| 1213 | ;; Common to GNU Emacs and XEmacs | ||
| 1214 | |||
| 1215 | ;; Add a mode indicater named "Dict" | ||
| 1216 | (defvar dictionary-tooltip-mode | ||
| 1217 | nil | ||
| 1218 | "Indicates wheather the dictionary tooltip mode is active") | ||
| 1219 | (nconc minor-mode-alist '((dictionary-tooltip-mode " Dict"))) | ||
| 1220 | |||
| 1221 | (defcustom dictionary-tooltip-dictionary | ||
| 1222 | nil | ||
| 1223 | "This dictionary to lookup words for tooltips" | ||
| 1224 | :group 'dictionary | ||
| 1225 | :type '(choice (const :tag "None" nil) string)) | ||
| 1226 | |||
| 1227 | (defun dictionary-definition (word &optional dictionary) | ||
| 1228 | (interactive) | ||
| 1229 | (unwind-protect | ||
| 1230 | (let ((dictionary (or dictionary dictionary-default-dictionary))) | ||
| 1231 | (dictionary-do-search word dictionary 'dictionary-read-definition t)) | ||
| 1232 | nil)) | ||
| 1233 | |||
| 1234 | (defun dictionary-read-definition (reply) | ||
| 1235 | (let ((list (dictionary-simple-split-string (dictionary-read-answer) "\n+"))) | ||
| 1236 | (mapconcat 'identity (cdr list) "\n"))) | ||
| 1237 | |||
| 1238 | (defconst dictionary-use-balloon-help | ||
| 1239 | (eval-when-compile | ||
| 1240 | (condition-case nil | ||
| 1241 | (require 'balloon-help) | ||
| 1242 | (error nil)))) | ||
| 1243 | |||
| 1244 | (make-variable-buffer-local 'dictionary-balloon-help-extent) | ||
| 1245 | |||
| 1246 | (if dictionary-use-balloon-help | ||
| 1247 | (progn | ||
| 1248 | |||
| 1249 | ;; The following definition are only valid for XEmacs with balloon-help | ||
| 1250 | |||
| 1251 | (defvar dictionary-balloon-help-position nil | ||
| 1252 | "Current position to lookup word") | ||
| 1253 | |||
| 1254 | (defun dictionary-balloon-help-store-position (event) | ||
| 1255 | (setq dictionary-balloon-help-position (event-point event))) | ||
| 1256 | |||
| 1257 | (defun dictionary-balloon-help-description (&rest extent) | ||
| 1258 | "Get the word from the cursor and lookup it" | ||
| 1259 | (if dictionary-balloon-help-position | ||
| 1260 | (let ((word (save-window-excursion | ||
| 1261 | (save-excursion | ||
| 1262 | (goto-char dictionary-balloon-help-position) | ||
| 1263 | (current-word))))) | ||
| 1264 | (let ((definition | ||
| 1265 | (dictionary-definition word dictionary-tooltip-dictionary))) | ||
| 1266 | (if definition | ||
| 1267 | (dictionary-decode-charset definition | ||
| 1268 | dictionary-tooltip-dictionary) | ||
| 1269 | nil))))) | ||
| 1270 | |||
| 1271 | (defvar dictionary-balloon-help-extent nil | ||
| 1272 | "The extent for activating the balloon help") | ||
| 1273 | |||
| 1274 | ;;;###autoload | ||
| 1275 | (defun dictionary-tooltip-mode (&optional arg) | ||
| 1276 | "Display tooltips for the current word" | ||
| 1277 | (interactive "P") | ||
| 1278 | (let* ((on (if arg | ||
| 1279 | (> (prefix-numeric-value arg) 0) | ||
| 1280 | (not dictionary-tooltip-mode)))) | ||
| 1281 | (make-local-variable 'dictionary-tooltip-mode) | ||
| 1282 | (if on | ||
| 1283 | ;; active mode | ||
| 1284 | (progn | ||
| 1285 | ;; remove old extend | ||
| 1286 | (if dictionary-balloon-help-extent | ||
| 1287 | (delete-extent dictionary-balloon-help-extent)) | ||
| 1288 | ;; create new one | ||
| 1289 | (setq dictionary-balloon-help-extent (make-extent (point-min) | ||
| 1290 | (point-max))) | ||
| 1291 | (set-extent-property dictionary-balloon-help-extent | ||
| 1292 | 'balloon-help | ||
| 1293 | 'dictionary-balloon-help-description) | ||
| 1294 | (set-extent-property dictionary-balloon-help-extent | ||
| 1295 | 'start-open nil) | ||
| 1296 | (set-extent-property dictionary-balloon-help-extent | ||
| 1297 | 'end-open nil) | ||
| 1298 | (add-hook 'mouse-motion-hook | ||
| 1299 | 'dictionary-balloon-help-store-position)) | ||
| 1300 | |||
| 1301 | ;; deactivate mode | ||
| 1302 | (if dictionary-balloon-help-extent | ||
| 1303 | (delete-extent dictionary-balloon-help-extent)) | ||
| 1304 | (remove-hook 'mouse-motion-hook | ||
| 1305 | 'dictionary-balloon-help-store-position)) | ||
| 1306 | (setq dictionary-tooltip-mode on) | ||
| 1307 | (balloon-help-minor-mode on))) | ||
| 1308 | |||
| 1309 | ) ;; end of XEmacs part | ||
| 1310 | |||
| 1311 | (defvar global-dictionary-tooltip-mode | ||
| 1312 | nil) | ||
| 1313 | |||
| 1314 | ;;; Tooltip support for GNU Emacs | ||
| 1315 | (defun dictionary-display-tooltip (event) | ||
| 1316 | "Search the current word in the `dictionary-tooltip-dictionary'." | ||
| 1317 | (interactive "e") | ||
| 1318 | (if dictionary-tooltip-dictionary | ||
| 1319 | (let ((word (save-window-excursion | ||
| 1320 | (save-excursion | ||
| 1321 | (mouse-set-point event) | ||
| 1322 | (current-word))))) | ||
| 1323 | (let ((definition | ||
| 1324 | (dictionary-definition word dictionary-tooltip-dictionary))) | ||
| 1325 | (if definition | ||
| 1326 | (tooltip-show | ||
| 1327 | (dictionary-decode-charset definition | ||
| 1328 | dictionary-tooltip-dictionary))) | ||
| 1329 | t)) | ||
| 1330 | nil)) | ||
| 1331 | |||
| 1332 | ;;;###autoload | ||
| 1333 | (defun dictionary-tooltip-mode (&optional arg) | ||
| 1334 | "Display tooltips for the current word" | ||
| 1335 | (interactive "P") | ||
| 1336 | (require 'tooltip) | ||
| 1337 | (let ((on (if arg | ||
| 1338 | (> (prefix-numeric-value arg) 0) | ||
| 1339 | (not dictionary-tooltip-mode)))) | ||
| 1340 | (make-local-variable 'dictionary-tooltip-mode) | ||
| 1341 | (setq dictionary-tooltip-mode on) | ||
| 1342 | ;; make sure that tooltip is still (global available) even is on | ||
| 1343 | ;; if nil | ||
| 1344 | (tooltip-mode 1) | ||
| 1345 | (add-hook 'tooltip-hook 'dictionary-display-tooltip) | ||
| 1346 | (make-local-variable 'track-mouse) | ||
| 1347 | (setq track-mouse on))) | ||
| 1348 | |||
| 1349 | ;;;###autoload | ||
| 1350 | (defun global-dictionary-tooltip-mode (&optional arg) | ||
| 1351 | "Enable/disable dictionary-tooltip-mode for all buffers" | ||
| 1352 | (interactive "P") | ||
| 1353 | (require 'tooltip) | ||
| 1354 | (let* ((on (if arg (> (prefix-numeric-value arg) 0) | ||
| 1355 | (not global-dictionary-tooltip-mode))) | ||
| 1356 | (hook-fn (if on 'add-hook 'remove-hook))) | ||
| 1357 | (setq global-dictionary-tooltip-mode on) | ||
| 1358 | (tooltip-mode 1) | ||
| 1359 | (funcall hook-fn 'tooltip-hook 'dictionary-display-tooltip) | ||
| 1360 | (setq-default dictionary-tooltip-mode on) | ||
| 1361 | (setq-default track-mouse on))) | ||
| 1362 | |||
| 1363 | ) ;; end of GNU Emacs part | ||
| 1364 | |||
| 1365 | (provide 'dictionary) | ||
| 1366 | |||
| 1367 | ;;; dictionary.el ends here | ||
diff --git a/lisp/net/link.el b/lisp/net/link.el new file mode 100644 index 00000000000..30eadb10176 --- /dev/null +++ b/lisp/net/link.el | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | ;;; link.el --- Hypertext links in text buffers | ||
| 2 | |||
| 3 | ;; Author: Torsten Hilbrich <torsten.hilbrich@gmx.net> | ||
| 4 | ;; Keywords: interface, hypermedia | ||
| 5 | ;; Version: 1.11 | ||
| 6 | |||
| 7 | ;; This file is free software; you can redistribute it and/or modify | ||
| 8 | ;; it under the terms of the GNU General Public License as published by | ||
| 9 | ;; the Free Software Foundation; either version 2, or (at your option) | ||
| 10 | ;; any later version. | ||
| 11 | |||
| 12 | ;; This file is distributed in the hope that it will be useful, | ||
| 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | ;; GNU General Public License for more details. | ||
| 16 | |||
| 17 | ;; You should have received a copy of the GNU General Public License | ||
| 18 | ;; along with GNU Emacs; see the file COPYING. If not, write to | ||
| 19 | ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
| 20 | ;; Boston, MA 02111-1307, USA. | ||
| 21 | |||
| 22 | ;;; Commentary: | ||
| 23 | |||
| 24 | ;; This file contains functions for using links in buffers. A link is | ||
| 25 | ;; a part of the buffer marked with a special face, beeing | ||
| 26 | ;; hightlighted while the mouse points to it and beeing activated when | ||
| 27 | ;; pressing return or clicking the button2. | ||
| 28 | |||
| 29 | ;; Which each link a function and some data are associated. Upon | ||
| 30 | ;; clicking the function is called with the data as only | ||
| 31 | ;; argument. Both the function and the data are stored in text | ||
| 32 | ;; properties. | ||
| 33 | ;; | ||
| 34 | ;; link-create-link - insert a new link for the text in the given range | ||
| 35 | ;; link-initialize-keymap - install the keybinding for selecting links | ||
| 36 | |||
| 37 | ;;; Code: | ||
| 38 | |||
| 39 | (eval-when-compile | ||
| 40 | (require 'cl)) | ||
| 41 | |||
| 42 | (defun link-create-link (start end face function &optional data help) | ||
| 43 | "Create a link in the current buffer starting from `start' going to `end'. | ||
| 44 | The `face' is used for displaying, the `data' are stored together with the | ||
| 45 | link. Upon clicking the `function' is called with `data' as argument." | ||
| 46 | (let ((properties `(face ,face | ||
| 47 | mouse-face highlight | ||
| 48 | link t | ||
| 49 | link-data ,data | ||
| 50 | help-echo ,help | ||
| 51 | link-function ,function))) | ||
| 52 | (remove-text-properties start end properties) | ||
| 53 | (add-text-properties start end properties))) | ||
| 54 | |||
| 55 | (defun link-insert-link (text face function &optional data help) | ||
| 56 | "Insert the `text' at point to be formatted as link. | ||
| 57 | The `face' is used for displaying, the `data' are stored together with the | ||
| 58 | link. Upon clicking the `function' is called with `data' as argument." | ||
| 59 | (let ((start (point))) | ||
| 60 | (insert text) | ||
| 61 | (link-create-link start (point) face function data help))) | ||
| 62 | |||
| 63 | (defun link-selected (&optional all) | ||
| 64 | "Is called upon clicking or otherwise visiting the link." | ||
| 65 | (interactive) | ||
| 66 | |||
| 67 | (let* ((properties (text-properties-at (point))) | ||
| 68 | (function (plist-get properties 'link-function)) | ||
| 69 | (data (plist-get properties 'link-data))) | ||
| 70 | (if function | ||
| 71 | (funcall function data all)))) | ||
| 72 | |||
| 73 | (defun link-selected-all () | ||
| 74 | "Called for meta clicking the link" | ||
| 75 | (interactive) | ||
| 76 | (link-selected 'all)) | ||
| 77 | |||
| 78 | (defun link-mouse-click (event &optional all) | ||
| 79 | "Is called upon clicking the link." | ||
| 80 | (interactive "@e") | ||
| 81 | |||
| 82 | (mouse-set-point event) | ||
| 83 | (link-selected)) | ||
| 84 | |||
| 85 | (defun link-mouse-click-all (event) | ||
| 86 | "Is called upon meta clicking the link." | ||
| 87 | (interactive "@e") | ||
| 88 | |||
| 89 | (mouse-set-point event) | ||
| 90 | (link-selected-all)) | ||
| 91 | |||
| 92 | (defun link-next-link () | ||
| 93 | "Return the position of the next link or nil if there is none" | ||
| 94 | (let* ((pos (point)) | ||
| 95 | (pos (next-single-property-change pos 'link))) | ||
| 96 | (if pos | ||
| 97 | (if (text-property-any pos (min (1+ pos) (point-max)) 'link t) | ||
| 98 | pos | ||
| 99 | (next-single-property-change pos 'link)) | ||
| 100 | nil))) | ||
| 101 | |||
| 102 | |||
| 103 | (defun link-prev-link () | ||
| 104 | "Return the position of the previous link or nil if there is none" | ||
| 105 | (let* ((pos (point)) | ||
| 106 | (pos (previous-single-property-change pos 'link))) | ||
| 107 | (if pos | ||
| 108 | (if (text-property-any pos (1+ pos) 'link t) | ||
| 109 | pos | ||
| 110 | (let ((val (previous-single-property-change pos 'link))) | ||
| 111 | (if val | ||
| 112 | val | ||
| 113 | (text-property-any (point-min) (1+ (point-min)) 'link t)))) | ||
| 114 | nil))) | ||
| 115 | |||
| 116 | (defun link-initialize-keymap (keymap) | ||
| 117 | "Defines the necessary bindings inside keymap" | ||
| 118 | |||
| 119 | (if (and (boundp 'running-xemacs) running-xemacs) | ||
| 120 | (progn | ||
| 121 | (define-key keymap [button2] 'link-mouse-click) | ||
| 122 | (define-key keymap [(meta button2)] 'link-mouse-click-all)) | ||
| 123 | (define-key keymap [mouse-2] 'link-mouse-click) | ||
| 124 | (define-key keymap [M-mouse-2] 'link-mouse-click-all)) | ||
| 125 | (define-key keymap "\r" 'link-selected) | ||
| 126 | (define-key keymap "\M-\r" 'link-selected-all)) | ||
| 127 | |||
| 128 | (provide 'link) | ||
| 129 | ;;; link.el ends here | ||