diff options
| author | Kenichi Handa | 2005-03-16 11:45:07 +0000 |
|---|---|---|
| committer | Kenichi Handa | 2005-03-16 11:45:07 +0000 |
| commit | 40e89154970eb0dc0cefa10eff54f25e76aa4fb0 (patch) | |
| tree | 343c65dbd610458451699f409033f526112c63ec | |
| parent | ada7cbe1e3aa9009e21b4752223d0523986e92bd (diff) | |
| download | emacs-40e89154970eb0dc0cefa10eff54f25e76aa4fb0.tar.gz emacs-40e89154970eb0dc0cefa10eff54f25e76aa4fb0.zip | |
New file.
| -rw-r--r-- | lisp/international/robin.el | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/lisp/international/robin.el b/lisp/international/robin.el new file mode 100644 index 00000000000..19c5a2a60c6 --- /dev/null +++ b/lisp/international/robin.el | |||
| @@ -0,0 +1,569 @@ | |||
| 1 | ;;; robin.el --- yet another input method (smaller than quail) | ||
| 2 | |||
| 3 | ;; Copyright (C) 2003, 2004, 2005 | ||
| 4 | ;; National Institute of Advanced Industrial Science and Technology (AIST) | ||
| 5 | ;; Registration Number: H15PRO 110 | ||
| 6 | |||
| 7 | ;; Author: TAKAHASHI Naoto <ntakahas@m17n.org> | ||
| 8 | ;; Keywords: mule, multilingual, input method | ||
| 9 | |||
| 10 | ;; This program is free software; you can redistribute it and/or | ||
| 11 | ;; modify it under the terms of the GNU General Public License as | ||
| 12 | ;; published by the Free Software Foundation; either version 2, or (at | ||
| 13 | ;; your option) any later version. | ||
| 14 | |||
| 15 | ;; This program is distributed in the hope that it will be useful, but | ||
| 16 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 18 | ;; General Public License for more details. | ||
| 19 | |||
| 20 | ;; You should have received a copy of the GNU General Public License | ||
| 21 | ;; along with this program; see the file COPYING. If not, write to | ||
| 22 | ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
| 23 | ;; Boston, MA 02111-1307, USA. | ||
| 24 | |||
| 25 | ;;; Comentary: | ||
| 26 | |||
| 27 | ;; Functionalities | ||
| 28 | ;; --------------- | ||
| 29 | |||
| 30 | ;; Robin is a new input method for GNU Emacs. It has three | ||
| 31 | ;; functionalities: | ||
| 32 | |||
| 33 | ;; 1. It serves as a simple input method. When the user types an ASCII | ||
| 34 | ;; key sequence, robin converts it into a string. This functionality | ||
| 35 | ;; is most likely used to input non-ASCII characters. | ||
| 36 | |||
| 37 | ;; 2. It converts existing buffer substring into another string. | ||
| 38 | ;; This functionality is similar to the 1. above, but the input is | ||
| 39 | ;; buffer substring rather than key strokes. | ||
| 40 | |||
| 41 | ;; 3. It offers reverse conversion. Each character produced by a | ||
| 42 | ;; robin rule can hold the original ASCII sequence as a | ||
| 43 | ;; char-code-property. | ||
| 44 | |||
| 45 | |||
| 46 | ;; How to define conversion rules | ||
| 47 | ;; ------------------------------ | ||
| 48 | |||
| 49 | ;; Each conversion rule belongs to a robin package. A robin package is | ||
| 50 | ;; identified by a string called package name. Use robin-define-package | ||
| 51 | ;; to define a robin package. | ||
| 52 | |||
| 53 | ;; (robin-define-package NAME DOCTSTRING | ||
| 54 | ;; (INPUT1 OUPUT1) | ||
| 55 | ;; (INPUT2 OUPUT2) | ||
| 56 | ;; ...) | ||
| 57 | |||
| 58 | ;; NAME is a string identifying the robin package. It often starts with a | ||
| 59 | ;; language name and followed by a method name. For example, | ||
| 60 | ;; french-postfix, greek-prefix, etc. | ||
| 61 | |||
| 62 | ;; DOCSTRING is a documentation string for the robin method. | ||
| 63 | |||
| 64 | ;; Each INPUTn is a string. It represents a transliteration of the | ||
| 65 | ;; corresponding OUTPUTn. | ||
| 66 | |||
| 67 | ;; Each OUTPUTn is a string or a character that is to be inserted as the | ||
| 68 | ;; result of conversion. | ||
| 69 | |||
| 70 | ;; Neither INPUT* nor OUTPUT* are evaluated. Do not use a variable or a | ||
| 71 | ;; function in those parts. Instead, use a string or character literal | ||
| 72 | ;; directly. | ||
| 73 | |||
| 74 | ;; If multiple rules have the same input pattern but different output | ||
| 75 | ;; patterns, only the latest definition is effective. | ||
| 76 | |||
| 77 | |||
| 78 | ;; Example | ||
| 79 | ;; ------- | ||
| 80 | |||
| 81 | ;; (robin-define-package "german-example" | ||
| 82 | ;; "An example for German | ||
| 83 | |||
| 84 | ;; AE -> Ä OE -> Ö UE -> Ü | ||
| 85 | ;; ae -> ä oe -> ö ue -> ü ss -> ß | ||
| 86 | |||
| 87 | ;; Repeat E or S to input itself. | ||
| 88 | |||
| 89 | ;; AEE -> AE OEE -> OE UEE -> UE | ||
| 90 | ;; aee -> ae oee -> oe uee -> ue sss -> ss" | ||
| 91 | |||
| 92 | ;; ("AE" ?Ä) | ||
| 93 | ;; ("OE" ?Ö) | ||
| 94 | ;; ("UE" ?Ü) | ||
| 95 | ;; ("ae" ?ä) | ||
| 96 | ;; ("oe" ?ö) | ||
| 97 | ;; ("ue" ?ü) | ||
| 98 | ;; ("ss" ?ß) | ||
| 99 | |||
| 100 | ;; ("AEE" "AE") | ||
| 101 | ;; ("OEE" "OE") | ||
| 102 | ;; ("UEE" "UE") | ||
| 103 | ;; ("aee" "ae") | ||
| 104 | ;; ("oee" "oe") | ||
| 105 | ;; ("uee" "ue") | ||
| 106 | ;; ("sss" "ss") | ||
| 107 | ;; ) | ||
| 108 | |||
| 109 | |||
| 110 | ;; Using robin as an input method | ||
| 111 | ;; ------------------------------ | ||
| 112 | |||
| 113 | ;; To use a defined robin package as an input method, register it with | ||
| 114 | ;; the register-input-method function. For example, | ||
| 115 | |||
| 116 | ;; (register-input-method | ||
| 117 | ;; "german-example" | ||
| 118 | ;; "german" | ||
| 119 | ;; 'robin-use-package | ||
| 120 | ;; "de" | ||
| 121 | ;; "An example for German") | ||
| 122 | |||
| 123 | ;; The first argument is the robin package name. | ||
| 124 | |||
| 125 | ;; The second argument is the language environment for which this robin | ||
| 126 | ;; package is used. | ||
| 127 | |||
| 128 | ;; Use the symbol `robin-use-package' as the third argument. | ||
| 129 | |||
| 130 | ;; The fourth argument is the prompt that appears in modeline when this | ||
| 131 | ;; input method is active. | ||
| 132 | |||
| 133 | ;; The fifth argument is a documentation string; it may or may not be | ||
| 134 | ;; identical to the one that you specified in robin-define-package. | ||
| 135 | |||
| 136 | ;; You can activate the robin input method by typing | ||
| 137 | |||
| 138 | ;; C-u C-\ german-example RET | ||
| 139 | |||
| 140 | ;; Just like a quail package, only C-\ suffices for subsequent | ||
| 141 | ;; invocation. | ||
| 142 | |||
| 143 | |||
| 144 | ;; Using robin as a buffer translator | ||
| 145 | ;; ---------------------------------- | ||
| 146 | |||
| 147 | ;; To transliterate buffer substring, use the following functions. | ||
| 148 | |||
| 149 | ;; (robin-convert-buffer &optional name) | ||
| 150 | |||
| 151 | ;; Convert the content of current buffer using a robin package. | ||
| 152 | |||
| 153 | ;; NAME, if given, is a string specifying a robin package. If NAME is | ||
| 154 | ;; not given or nil, the value of `robin-current-package-name' is used. | ||
| 155 | |||
| 156 | ;; (robin-convert-region begin end &optional name) | ||
| 157 | |||
| 158 | ;; Convert the region using a robin package. | ||
| 159 | |||
| 160 | ;; NAME, if given, is a string specifying a robin package. If NAME is | ||
| 161 | ;; not given or nil, the value of `robin-current-package-name' is used. | ||
| 162 | |||
| 163 | |||
| 164 | ;; Reverse conversion | ||
| 165 | ;; ------------------ | ||
| 166 | |||
| 167 | ;; If the output pattern defined in a robin rule is a character, robin | ||
| 168 | ;; gives to the character a char-code-property whose key is the symbol | ||
| 169 | ;; representation of the robin package name and whose value is the input | ||
| 170 | ;; pattern of that character. For example, with the "german-example" | ||
| 171 | ;; definition above, | ||
| 172 | |||
| 173 | ;; (get-char-code-property ?Ä 'german-example) => "AE" | ||
| 174 | |||
| 175 | ;; etc. | ||
| 176 | |||
| 177 | ;; If you do not want to assign a char-code-property to a character, use | ||
| 178 | ;; a string of length one as the output pattern, e.g. | ||
| 179 | |||
| 180 | ;; (robin-define-package "german-example2" | ||
| 181 | ;; "Another example for German." | ||
| 182 | |||
| 183 | ;; ("AE" "Ä") | ||
| 184 | ;; ("OE" "Ö") | ||
| 185 | ;; ...) | ||
| 186 | |||
| 187 | ;; Then | ||
| 188 | |||
| 189 | ;; (get-char-code-property ?Ä 'german-example2) => nil | ||
| 190 | |||
| 191 | ;; etc. | ||
| 192 | |||
| 193 | ;; If multiple input patterns in a robin package generate the same | ||
| 194 | ;; character, the lastly used input pattern is given as the value of the | ||
| 195 | ;; char-code-property. | ||
| 196 | |||
| 197 | ;; There are two functions for reverse conversion. | ||
| 198 | |||
| 199 | ;; (robin-invert-buffer &optional name) | ||
| 200 | |||
| 201 | ;; Apply reverse conversion to the content of current buffer. NAME, if | ||
| 202 | ;; given, is a string specifying a robin package. If NAME is not given | ||
| 203 | ;; or nil, the value of `robin-current-package-name' is used. | ||
| 204 | |||
| 205 | ;; (robin-invert-region begin end &optional name) | ||
| 206 | |||
| 207 | ;; Apply reverse conversion to the region. NAME, if given, is a string | ||
| 208 | ;; specifying a robin package. If NAME is not given or nil, the value of | ||
| 209 | ;; `robin-current-package-name' is used. | ||
| 210 | |||
| 211 | |||
| 212 | ;; Modifying an existing rule | ||
| 213 | ;; -------------------------- | ||
| 214 | |||
| 215 | ;; Use the robin-modify-package function to modify a rule already defined | ||
| 216 | ;; in a Robin package. | ||
| 217 | |||
| 218 | ;; (robin-modify-package name input output) | ||
| 219 | |||
| 220 | ;; Change a rule in an already defined Robin package. | ||
| 221 | ;; NAME is the string specifying a robin package. | ||
| 222 | ;; INPUT is a string that specifies the input pattern. | ||
| 223 | ;; OUTPUT is either a character or a string to be generated. | ||
| 224 | |||
| 225 | |||
| 226 | ;; The name of the game | ||
| 227 | ;; -------------------- | ||
| 228 | |||
| 229 | ;; As stated in Murphy's law, it took longer than expected to develop the | ||
| 230 | ;; very first version of Japanese input subsystem in NEmacs (Nihongo | ||
| 231 | ;; Emacs). So the subsystem was named "TAMAGO", which is an acronym of | ||
| 232 | ;; "TAkusan Matasete GOmennasai" (Sorry to have kept you waiting so | ||
| 233 | ;; long). "Tamago" as a Japanese word means "egg", so the word "egg" was | ||
| 234 | ;; also used for related filenames and function names. | ||
| 235 | |||
| 236 | ;; Since it was designed to input CJK characters, Egg was rather big as a | ||
| 237 | ;; subsystem. So later in Mule (Multilingual Enhancement to GNU Emacs), | ||
| 238 | ;; we designed and implemented a smaller input subsystem. We had to give | ||
| 239 | ;; it a name. "So, what's smaller than an egg?" "A quail egg, of | ||
| 240 | ;; course." Therefore it was named "quail". | ||
| 241 | |||
| 242 | ;; As time went by, quail became more and more complicated. That | ||
| 243 | ;; tendency was inevitable as long as we support CJK input. However, if | ||
| 244 | ;; we can limit ourselves to non-CJK characters, a much simpler | ||
| 245 | ;; transliteration mechanism suffices. So I wrote "robin", whose name | ||
| 246 | ;; was chosen because a robin is smaller than a quail. I could name it | ||
| 247 | ;; "hummingbird" or "nightingale", but those spellings seemed too long. | ||
| 248 | |||
| 249 | |||
| 250 | ;;; Code: | ||
| 251 | |||
| 252 | (defvar robin-package-alist nil | ||
| 253 | "List of robin packages. | ||
| 254 | A robin pacakge is of the form (NAME DOCSTRING &rest RULES). | ||
| 255 | NAME is a string specifying a particular robin package. | ||
| 256 | DOCSTRING is a documentation string for the robin package. | ||
| 257 | |||
| 258 | RULE is of the form (KEY OUTPUT &rest rules). | ||
| 259 | KEY is a string. | ||
| 260 | OUTPUT is a character or a string. | ||
| 261 | For example, if you evaluate the following, | ||
| 262 | |||
| 263 | (robin-define-package \"test\" \"Uppercase input characters\" | ||
| 264 | (\"a\" \"A\") | ||
| 265 | (\"ab\" \"AB\") | ||
| 266 | (\"ac\" \"AC\") | ||
| 267 | (\"acd\" \"ACD\") | ||
| 268 | (\"ace\" \"ACE\") | ||
| 269 | (\"b\" \"B\")) | ||
| 270 | |||
| 271 | this robin package will be the following. | ||
| 272 | |||
| 273 | (\"test\" \"Uppercase input characters\" | ||
| 274 | (?a \"A\" | ||
| 275 | (?b \"AB\") | ||
| 276 | (?c \"AC\" | ||
| 277 | (?d \"ACD\") | ||
| 278 | (?e \"ACE\"))) | ||
| 279 | (?b \"B\")) | ||
| 280 | ") | ||
| 281 | |||
| 282 | ;;;###autoload | ||
| 283 | (defmacro robin-define-package (name docstring &rest rules) | ||
| 284 | "Define a robin package. | ||
| 285 | |||
| 286 | NAME is the string of this robin package. | ||
| 287 | DOCSTRING is the documentation string of this robin package. | ||
| 288 | Each RULE is of the form (INPUT OUTPUT) where INPUT is a string and | ||
| 289 | OUTPUT is either a character or a string. RULES are not evaluated. | ||
| 290 | |||
| 291 | If there already exists a robin package whose name is NAME, the new | ||
| 292 | one replaces the old one." | ||
| 293 | |||
| 294 | (let ((old (assoc name robin-package-alist)) | ||
| 295 | (new (list name "")) ; "" as a fake output | ||
| 296 | input output) | ||
| 297 | (dolist (r rules) | ||
| 298 | (setq input (car r) | ||
| 299 | output (cadr r)) | ||
| 300 | (robin-add-rule name new input output) | ||
| 301 | (cond | ||
| 302 | ((not (stringp input)) | ||
| 303 | (error "Bad input sequence %S" r)) | ||
| 304 | ((characterp output) | ||
| 305 | (put-char-code-property output (intern name) input)) | ||
| 306 | ((not (stringp output)) | ||
| 307 | (error "Bad output pattern %S" r)))) | ||
| 308 | (setcar (cdr new) docstring) ; replace "" above with real docstring | ||
| 309 | (if old | ||
| 310 | (setcdr old (cdr new)) | ||
| 311 | (setq robin-package-alist | ||
| 312 | (cons new robin-package-alist))) | ||
| 313 | `(setq robin-package-alist ',robin-package-alist))) | ||
| 314 | |||
| 315 | ;;;###autoload | ||
| 316 | (defun robin-modify-package (name input output) | ||
| 317 | "Change a rule in an already defined robin package. | ||
| 318 | |||
| 319 | NAME is the string specifying a robin package. | ||
| 320 | INPUT is a string that specifies the input pattern. | ||
| 321 | OUTPUT is either a character or a string to be generated." | ||
| 322 | |||
| 323 | (let ((tree (assoc name robin-package-alist)) | ||
| 324 | docstring) | ||
| 325 | (if (not tree) | ||
| 326 | (error "No such robin package") | ||
| 327 | (setq docstring (cadr tree)) | ||
| 328 | (setcar (cdr tree) "") | ||
| 329 | (robin-add-rule name tree input output) | ||
| 330 | (setcar (cdr tree) docstring) | ||
| 331 | (if (characterp output) | ||
| 332 | (put-char-code-property output (intern name) input)))) | ||
| 333 | output) | ||
| 334 | |||
| 335 | (defun robin-add-rule (name tree input output) | ||
| 336 | "Add translation rule (INPUT OUTPUT) to TREE whose name is NAME. | ||
| 337 | Internal use only." | ||
| 338 | |||
| 339 | (let* ((head (aref input 0)) | ||
| 340 | (branch (assoc head tree)) | ||
| 341 | (sofar (cadr tree))) | ||
| 342 | |||
| 343 | (if (= (length input) 1) | ||
| 344 | (if branch | ||
| 345 | |||
| 346 | ;; A definition already exists for this input. | ||
| 347 | (progn | ||
| 348 | (setcar (cdr branch) output) | ||
| 349 | ;; Cancel char-code-property for old definition. | ||
| 350 | (when (characterp output) | ||
| 351 | (put-char-code-property output (intern name) nil))) | ||
| 352 | |||
| 353 | ;; New definition for this input. | ||
| 354 | (setcdr (last tree) (list (list head output)))) | ||
| 355 | |||
| 356 | (unless branch | ||
| 357 | (if (characterp sofar) | ||
| 358 | (setq sofar (char-to-string sofar))) | ||
| 359 | (setq branch | ||
| 360 | (list head | ||
| 361 | (concat sofar | ||
| 362 | (char-to-string head)))) | ||
| 363 | (setcdr (last tree) (list branch))) | ||
| 364 | |||
| 365 | (robin-add-rule name branch (substring input 1) output)))) | ||
| 366 | |||
| 367 | ;;; Interactive use | ||
| 368 | |||
| 369 | (defvar robin-mode nil | ||
| 370 | "If non-nil, `robin-input-method' is active.") | ||
| 371 | (make-variable-buffer-local 'robin-mode) | ||
| 372 | |||
| 373 | (defvar robin-current-package-name nil | ||
| 374 | "String representing the name of the current robin package. | ||
| 375 | Nil means no packages is selected.") | ||
| 376 | (make-variable-buffer-local 'robin-current-package-name) | ||
| 377 | |||
| 378 | ;;;###autoload | ||
| 379 | (defun robin-use-package (name) | ||
| 380 | "Start using robin package NAME, which is a string." | ||
| 381 | |||
| 382 | (let ((package (assoc name robin-package-alist))) | ||
| 383 | (unless package | ||
| 384 | (error "No such robin package")) | ||
| 385 | (setq robin-current-package-name name) | ||
| 386 | (robin-activate))) | ||
| 387 | |||
| 388 | (defun robin-inactivate () | ||
| 389 | "Inactivate robin input method." | ||
| 390 | |||
| 391 | (interactive) | ||
| 392 | (robin-activate -1)) | ||
| 393 | |||
| 394 | (defun robin-activate (&optional arg) | ||
| 395 | "Activate robin input method. | ||
| 396 | |||
| 397 | With ARG, activate robin input method iff ARG is positive. | ||
| 398 | |||
| 399 | While this input method is active, the variable | ||
| 400 | `input-method-function' is bound to the function `robin-input-method'." | ||
| 401 | (if (and arg | ||
| 402 | (< (prefix-numeric-value arg) 0)) | ||
| 403 | |||
| 404 | ;; inactivate robin input method. | ||
| 405 | (unwind-protect | ||
| 406 | (progn | ||
| 407 | (setq robin-mode nil) | ||
| 408 | (setq describe-current-input-method-function nil) | ||
| 409 | (run-hooks 'robin-inactivate-hook)) | ||
| 410 | (kill-local-variable 'input-method-function)) | ||
| 411 | |||
| 412 | ;; activate robin input method. | ||
| 413 | (setq robin-mode t | ||
| 414 | describe-current-input-method-function 'robin-help | ||
| 415 | inactivate-current-input-method-function 'robin-inactivate) | ||
| 416 | (if (eq (selected-window) (minibuffer-window)) | ||
| 417 | (add-hook 'minibuffer-exit-hook 'robin-exit-from-minibuffer)) | ||
| 418 | (run-hooks 'input-method-activate-hook | ||
| 419 | 'robin-activate-hook) | ||
| 420 | (set (make-local-variable 'input-method-function) | ||
| 421 | 'robin-input-method))) | ||
| 422 | |||
| 423 | (defun robin-exit-from-minibuffer () | ||
| 424 | (inactivate-input-method) | ||
| 425 | (if (<= (minibuffer-depth) 1) | ||
| 426 | (remove-hook 'minibuffer-exit-hook 'robin-exit-from-minibuffer))) | ||
| 427 | |||
| 428 | (defun robin-input-method (key) | ||
| 429 | "Interpret typed key sequence and insert into buffer." | ||
| 430 | |||
| 431 | (if (or buffer-read-only | ||
| 432 | overriding-terminal-local-map | ||
| 433 | overriding-local-map) | ||
| 434 | (list key) | ||
| 435 | |||
| 436 | (let ((echo-keystrokes 0) | ||
| 437 | (input-method-function nil) | ||
| 438 | (start (point)) | ||
| 439 | (tree (cddr (assoc robin-current-package-name robin-package-alist))) | ||
| 440 | branch | ||
| 441 | output) | ||
| 442 | |||
| 443 | (while (setq branch (assq key tree)) | ||
| 444 | (delete-region start (point)) | ||
| 445 | (insert (setq output (cadr branch))) | ||
| 446 | (setq tree (cddr branch)) | ||
| 447 | (if tree | ||
| 448 | (setq key (read-event)) | ||
| 449 | (setq key nil))) | ||
| 450 | |||
| 451 | (if (null output) | ||
| 452 | ;; body of the `while' above was not executed | ||
| 453 | (list key) | ||
| 454 | (delete-region start (point)) | ||
| 455 | (if key | ||
| 456 | (setq unread-command-events (list key))) | ||
| 457 | (if (stringp output) | ||
| 458 | (string-to-list output) | ||
| 459 | (list output)))))) | ||
| 460 | |||
| 461 | (defun robin-help () | ||
| 462 | "Display the docstring of the current robin package." | ||
| 463 | |||
| 464 | (interactive) | ||
| 465 | (let ((buf (get-buffer-create "*Robin Help*")) | ||
| 466 | (doc (cadr (assoc robin-current-package-name robin-package-alist)))) | ||
| 467 | (set-buffer buf) | ||
| 468 | (erase-buffer) | ||
| 469 | (insert doc) | ||
| 470 | (goto-char (point-min)) | ||
| 471 | (display-buffer buf))) | ||
| 472 | |||
| 473 | ;;; Batch mode | ||
| 474 | |||
| 475 | (defun robin-convert-buffer (&optional name) | ||
| 476 | "Convert the content of current buffer using a robin package. | ||
| 477 | NAME, if given, is a string specifying a robin package. If NAME | ||
| 478 | is not given or nil, the value of `robin-current-package-name' is | ||
| 479 | used." | ||
| 480 | |||
| 481 | (interactive "*") | ||
| 482 | (robin-convert-region (point-min) (point-max) name)) | ||
| 483 | |||
| 484 | (defun robin-convert-region (begin end &optional name) | ||
| 485 | "Convert the region using a robin package. | ||
| 486 | NAME, if given, is a string specifying a robin package. If NAME | ||
| 487 | is not given or nil, the value of `robin-current-package-name' is | ||
| 488 | used." | ||
| 489 | |||
| 490 | (interactive "*r") | ||
| 491 | (or name | ||
| 492 | (setq name robin-current-package-name) | ||
| 493 | (error "No robin package specified")) | ||
| 494 | |||
| 495 | (let ((tree (assoc name robin-package-alist))) | ||
| 496 | (unless tree | ||
| 497 | (error "No such robin package")) | ||
| 498 | |||
| 499 | (save-excursion | ||
| 500 | (save-restriction | ||
| 501 | (narrow-to-region begin end) | ||
| 502 | (goto-char (point-min)) | ||
| 503 | (while (not (eobp)) | ||
| 504 | (robin-convert-region-internal tree)))))) | ||
| 505 | |||
| 506 | (defun robin-convert-region-internal (tree) | ||
| 507 | "Apply a robin rule defined in TREE to the current point. | ||
| 508 | Use the longest match method to select a rule." | ||
| 509 | |||
| 510 | (let ((begin (point)) | ||
| 511 | end branch) | ||
| 512 | (while (setq branch (assq (following-char) tree)) | ||
| 513 | (setq tree branch) | ||
| 514 | (forward-char 1)) | ||
| 515 | |||
| 516 | (setq end (point)) | ||
| 517 | (if (= begin end) | ||
| 518 | ;; no matching rule found; leave it as it is | ||
| 519 | (forward-char 1) | ||
| 520 | ;; replace the string | ||
| 521 | (goto-char begin) | ||
| 522 | (insert (cadr tree)) | ||
| 523 | (delete-char (- end begin))))) | ||
| 524 | |||
| 525 | ;; for backward compatibility | ||
| 526 | |||
| 527 | (fset 'robin-transliterate-region 'robin-convert-region) | ||
| 528 | (fset 'robin-transliterate-buffer 'robin-convert-buffer) | ||
| 529 | |||
| 530 | ;;; Reverse conversion | ||
| 531 | |||
| 532 | (defun robin-invert-buffer (&optional name) | ||
| 533 | "Apply reverse conversion to the content of current buffer. | ||
| 534 | NAME, if given, is a string specifying a robin package. If NAME | ||
| 535 | is not given or nil, the value of `robin-current-package-name' is | ||
| 536 | used." | ||
| 537 | |||
| 538 | (interactive "*") | ||
| 539 | (robin-invert-region (point-min) (point-max) name)) | ||
| 540 | |||
| 541 | (defun robin-invert-region (begin end &optional name) | ||
| 542 | "Apply reverse conversion to the region. | ||
| 543 | NAME, if given, is a string specifying a robin package. If NAME | ||
| 544 | is not given or nil, the value of `robin-current-package-name' is | ||
| 545 | used." | ||
| 546 | |||
| 547 | (interactive "*r") | ||
| 548 | (or name | ||
| 549 | (setq name robin-current-package-name) | ||
| 550 | (error "No robin package specified")) | ||
| 551 | |||
| 552 | (setq name (intern name)) | ||
| 553 | (let (str) | ||
| 554 | (save-restriction | ||
| 555 | (narrow-to-region begin end) | ||
| 556 | (goto-char (point-min)) | ||
| 557 | (while (not (eobp)) | ||
| 558 | (if (not (setq str (get-char-code-property (following-char) name))) | ||
| 559 | (forward-char 1) | ||
| 560 | (insert str) | ||
| 561 | (delete-char 1)))))) | ||
| 562 | |||
| 563 | (provide 'robin) | ||
| 564 | |||
| 565 | ;;; Local Variables: | ||
| 566 | ;;; coding: utf-8-emacs | ||
| 567 | ;;; End: | ||
| 568 | |||
| 569 | ;;; robin.el ends here | ||