]> code.delx.au - gnu-emacs/blobdiff - lisp/calculator.el
; Merge from origin/emacs-25
[gnu-emacs] / lisp / calculator.el
index b1cda28bd25de5911108a133abf1c2378819038e..523bf98180ab6e46847a7cedbaf709f22a00b763 100644 (file)
@@ -1,6 +1,6 @@
 ;;; calculator.el --- a calculator for Emacs  -*- lexical-binding: t -*-
 
-;; Copyright (C) 1998, 2000-2015 Free Software Foundation, Inc.
+;; Copyright (C) 1998, 2000-2016 Free Software Foundation, Inc.
 
 ;; Author: Eli Barzilay <eli@barzilay.org>
 ;; Keywords: tools, convenience
@@ -161,6 +161,8 @@ This makes it possible to paste big integers since they will be read as
 floats, otherwise the Emacs reader will fail on them."
   :type  'boolean
   :group 'calculator)
+(make-obsolete-variable 'calculator-paste-decimals
+                        "it is no longer used." nil)
 
 (defcustom calculator-copy-displayer nil
   "If non-nil, this is any value that can be used for
@@ -290,7 +292,7 @@ user-defined operators, use `calculator-user-operators' instead.")
 (defvar calculator-operators nil
   "The calculator operators, each a list with:
 
-1. The key that is bound to for this operation, a string that is
+1. The key(s) that is bound to for this operation, a string that is
    used with `kbd';
 
 2. The displayed symbol for this function;
@@ -313,8 +315,8 @@ user-defined operators, use `calculator-user-operators' instead.")
 
 It it possible have a unary prefix version of a binary operator if it
 comes later in this list.  If the list begins with the symbol `nobind',
-then no key binding will take place -- this is only useful for
-predefined keys.
+then no key binding will take place -- this is only used for predefined
+keys.
 
 Use `calculator-user-operators' to add operators to this list, see its
 documentation for an example.")
@@ -371,74 +373,96 @@ Used for repeating operations in calculator-repR/L.")
           (list (cons ?e float-e) (cons ?p float-pi)))
   "The association list of calculator register values.")
 
-(defvar calculator-saved-global-map nil
-  "Saved global key map.")
-
 (defvar calculator-restart-other-mode nil
   "Used to hack restarting with the electric mode changed.")
 
 ;;;---------------------------------------------------------------------
 ;;; Key bindings
 
+(defun calculator-define-key (key cmd map)
+  ;; Arranges for unbound alphabetic keys to be used as their un/shifted
+  ;; versions if those are bound (mimics the usual Emacs global bindings).
+  ;; FIXME: We should adjust Emacs's native "fallback to unshifted binding"
+  ;; such that it can also be used here, rather than having to use a hack like
+  ;; this one.
+  (let* ((key  (if (stringp key) (kbd key) key))
+         (omap (keymap-parent map)))
+    (define-key map key cmd)
+    ;; "other" map, used for case-flipped bindings
+    (unless omap
+      (setq omap (make-sparse-keymap))
+      (suppress-keymap omap t)
+      (set-keymap-parent map omap))
+    (let ((m omap))
+      ;; Bind all case-flipped versions.
+      (dotimes (i (length key))
+        (let* ((c (aref key i))
+               (k (vector c))
+               (b (lookup-key m k))
+               (defkey (lambda (x)
+                         (define-key m k x)
+                         (when (and (characterp c)
+                                    (or (<= ?A c ?Z) (<= ?a c ?z)))
+                           (define-key m (vector (logxor 32 c)) x)))))
+          (cond ((= i (1- (length key)))
+                 ;; Prefer longer sequences.
+                 (unless (keymapp b) (funcall defkey cmd)))
+                ((keymapp b) (setq m b))
+                (t (let ((sub (make-sparse-keymap)))
+                     (funcall defkey sub)
+                     (setq m sub)))))))))
+
 (defvar calculator-mode-map
   (let ((map (make-sparse-keymap)))
     (suppress-keymap map t)
-    (define-key map "i" nil)
-    (define-key map "o" nil)
-    (let ((p
-           '((calculator-open-paren  "[")
-             (calculator-close-paren "]")
-             (calculator-op-or-exp   "+" "-" [kp-add] [kp-subtract])
-             (calculator-digit       "0" "1" "2" "3" "4" "5" "6" "7" "8"
-                                     "9" "a" "b" "c" "d" "f"
-                                     [kp-0] [kp-1] [kp-2] [kp-3] [kp-4]
-                                     [kp-5] [kp-6] [kp-7] [kp-8] [kp-9])
-             (calculator-op          [kp-divide] [kp-multiply])
-             (calculator-decimal     "." [kp-decimal])
-             (calculator-exp         "e")
-             (calculator-dec/deg-mode "D")
-             (calculator-set-register "s")
-             (calculator-get-register "g")
-             (calculator-radix-mode        "H" "X" "O" "B")
-             (calculator-radix-input-mode  "id" "ih" "ix" "io" "ib"
-                                           "iD" "iH" "iX" "iO" "iB")
-             (calculator-radix-output-mode "od" "oh" "ox" "oo" "ob"
-                                           "oD" "oH" "oX" "oO" "oB")
-             (calculator-rotate-displayer      "'")
-             (calculator-rotate-displayer-back "\"")
-             (calculator-displayer-prev        "{")
-             (calculator-displayer-next        "}")
-             (calculator-saved-up      [up] [?\C-p])
-             (calculator-saved-down    [down] [?\C-n])
-             (calculator-quit          "q" [?\C-g])
-             (calculator-enter         [enter] [linefeed] [kp-enter]
-                                       [return] [?\r] [?\n])
-             (calculator-save-on-list  " " [space])
-             (calculator-clear-saved   [?\C-c] [(control delete)])
-             (calculator-save-and-quit [(control return)]
-                                       [(control kp-enter)])
-             (calculator-paste         [insert] [(shift insert)]
-                                       [paste] [mouse-2] [?\C-y])
-             (calculator-clear         [delete] [?\C-?] [?\C-d])
-             (calculator-help          [?h] [??] [f1] [help])
-             (calculator-copy          [(control insert)] [copy])
-             (calculator-backspace     [backspace])
-             )))
-      (while p
-        ;; reverse the keys so earlier definitions come last -- makes
-        ;; the more sensible bindings visible in the menu
-        (let ((func (caar p)) (keys (reverse (cdar p))))
-          (while keys
-            (define-key map (car keys) func)
-            (setq keys (cdr keys))))
-        (setq p (cdr p))))
+    (dolist (x '((calculator-digit
+                  "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "a" "b" "c"
+                  "d" "f" "<kp-0>" "<kp-1>" "<kp-2>" "<kp-3>" "<kp-4>"
+                  "<kp-5>" "<kp-6>" "<kp-7>" "<kp-8>" "<kp-9>")
+                 (calculator-open-paren  "[")
+                 (calculator-close-paren "]")
+                 (calculator-op-or-exp   "+" "-"
+                                         "<kp-add>" "<kp-subtract>")
+                 (calculator-op          "<kp-divide>" "<kp-multiply>")
+                 (calculator-decimal     "." "<kp-decimal>")
+                 (calculator-exp         "e")
+                 (calculator-dec/deg-mode "D")
+                 (calculator-set-register "s")
+                 (calculator-get-register "g")
+                 (calculator-radix-mode        "H" "X" "O" "B")
+                 (calculator-radix-input-mode  "iD" "iH" "iX" "iO" "iB")
+                 (calculator-radix-output-mode "oD" "oH" "oX" "oO" "oB")
+                 (calculator-rotate-displayer      "'")
+                 (calculator-rotate-displayer-back "\"")
+                 (calculator-displayer-prev        "{")
+                 (calculator-displayer-next        "}")
+                 (calculator-saved-up     "<up>" "C-p")
+                 (calculator-saved-down   "<down>" "C-n")
+                 (calculator-quit         "q" "C-g")
+                 (calculator-enter        "<enter>" "<linefeed>"
+                                          "<kp-enter>" "<return>"
+                                          "RET" "LFD")
+                 (calculator-save-on-list "SPC" "<space>")
+                 (calculator-clear-saved  "C-c" "<C-delete>")
+                 (calculator-save-and-quit "<C-return>" "<C-kp-enter>")
+                 (calculator-paste        "<insert>" "<S-insert>"
+                                          "<paste>" "<mouse-2>" "C-y")
+                 (calculator-clear        "<delete>" "DEL" "C-d")
+                 (calculator-help         "h" "?" "<f1>" "<help>")
+                 (calculator-copy         "<C-insert>" "<copy>")
+                 (calculator-backspace    "<backspace>")
+                 ))
+      ;; reverse the keys so earlier definitions come last -- makes the
+      ;; more sensible bindings visible in the menu
+      (dolist (k (reverse (cdr x)))
+        (calculator-define-key k (car x) map)))
     (if calculator-bind-escape
-      (progn (define-key map [?\e] 'calculator-quit)
-             (define-key map [escape] 'calculator-quit))
-      (define-key map [?\e ?\e ?\e] 'calculator-quit))
+      (progn (calculator-define-key "ESC" 'calculator-quit map)
+             (calculator-define-key "<escape>" 'calculator-quit map))
+      (calculator-define-key "ESC ESC ESC" 'calculator-quit map))
     ;; make C-h work in text-mode
     (unless window-system
-      (define-key map [?\C-h] 'calculator-backspace))
+      (calculator-define-key "C-h" 'calculator-backspace map))
     ;; set up a menu
     (when (and calculator-use-menu (not (boundp 'calculator-menu)))
       (let ((radix-selectors
@@ -621,16 +645,17 @@ argument.
 hex/oct/bin modes can be set for input and for display separately.
 Another toggle-able mode is for using degrees instead of radians for
 trigonometric functions.
-The keys to switch modes are (`X' is shortcut for `H'):
+The keys to switch modes are (both `H' and `X' are for hex):
 * `D'             switch to all-decimal mode, or toggle degrees/radians
 * `B' `O' `H' `X' binary/octal/hexadecimal modes for input & display
 * `i' `o'         followed by one of `D' `B' `O' `H' `X' (case
                   insensitive) sets only the input or display radix mode
 The prompt indicates the current modes:
-* \"D=\": degrees mode;
-* \"?=\": (? is B/O/H) this is the radix for both input and output;
-* \"=?\": (? is B/O/H) the display radix (when input is decimal);
-* \"??\": (? is D/B/O/H) 1st char for input radix, 2nd for display.
+* \"==\": decimal mode (using radians);
+* \"D=\": decimal mode using degrees;
+* \"?=\": ? is B/O/H, the radix for both input and output;
+* \"=?\": ? is B/O/H, the display radix (with decimal input);
+* \"??\": ? is D/B/O/H, 1st char for input radix, 2nd for display.
 
 Also, the quote key can be used to switch display modes for decimal
 numbers (double-quote rotates back), and the two brace characters
@@ -690,19 +715,14 @@ See the documentation for `calculator-mode' for more information."
   (if calculator-electric-mode
     (save-window-excursion
       (require 'electric) (message nil) ; hide load message
-      (let (old-g-map old-l-map
-            (old-buf (window-buffer (minibuffer-window)))
+      (let ((old-buf (window-buffer (minibuffer-window)))
             (echo-keystrokes 0)
             (garbage-collection-messages nil)) ; no gc msg when electric
         (set-window-buffer (minibuffer-window) calculator-buffer)
         (select-window (minibuffer-window))
         (calculator-reset)
         (calculator-update-display)
-        (setq old-l-map (current-local-map)
-              old-g-map (current-global-map)
-              calculator-saved-global-map (current-global-map))
-        (use-local-map nil)
-        (use-global-map calculator-mode-map)
+        (use-local-map calculator-mode-map)
         (run-hooks 'calculator-mode-hook)
         (unwind-protect
             (catch 'calculator-done
@@ -713,9 +733,7 @@ See the documentation for `calculator-mode' for more information."
                nil
                (lambda (_x _y) (calculator-update-display))))
           (set-window-buffer (minibuffer-window) old-buf)
-          (kill-buffer calculator-buffer)
-          (use-local-map old-l-map)
-          (use-global-map old-g-map))))
+          (kill-buffer calculator-buffer))))
     (progn
       (cond
         ((not (get-buffer-window calculator-buffer))
@@ -782,23 +800,11 @@ Defaults to 1."
 Adds MORE-OPS to `calculator-operator', called initially to handle
 `calculator-initial-operators' and `calculator-user-operators'."
   (let ((added-ops nil))
-    (while more-ops
-      (unless (eq (caar more-ops) 'nobind)
-        (let ((i -1) (key (caar more-ops)))
-          ;; make sure the key is undefined, so it's easy to define
-          ;; prefix keys
-          (while (< (setq i (1+ i)) (length key))
-            (unless (keymapp (lookup-key calculator-mode-map
-                                         (substring key 0 (1+ i))))
-              (define-key calculator-mode-map (substring key 0 (1+ i))
-                nil)
-              (setq i (length key))))
-          (define-key calculator-mode-map key 'calculator-op)))
-      (push (if (eq (caar more-ops) 'nobind)
-              (cdar more-ops)
-              (car more-ops))
-            added-ops)
-      (setq more-ops (cdr more-ops)))
+    (dolist (op more-ops)
+      (unless (eq (car op) 'nobind)
+        (calculator-define-key (car op) 'calculator-op calculator-mode-map))
+      (push (if (eq (car op) 'nobind) (cdr op) op)
+            added-ops))
     ;; added-ops come first, but in correct order
     (setq calculator-operators
           (append (nreverse added-ops) calculator-operators))))
@@ -831,7 +837,7 @@ The result should not exceed the screen width."
                          (cond ((or in-r out-r)
                                 (concat (or in-r "=")
                                         (if (equal in-r out-r) "="
-                                            (or out-r "="))))
+                                            (or out-r "D"))))
                                (calculator-deg "D=")
                                (t "=="))))
          (expr
@@ -852,39 +858,13 @@ The result should not exceed the screen width."
   "Convert the given STR to a number, according to the value of
 `calculator-input-radix'."
   (if calculator-input-radix
-    (let ((radix
-           (cdr (assq calculator-input-radix
-                      '((bin . 2) (oct . 8) (hex . 16)))))
-          (i -1) (value 0) (new-value 0))
-      ;; assume mostly valid input (e.g., characters in range)
-      (while (< (setq i (1+ i)) (length str))
-        (setq new-value
-              (let* ((ch (upcase (aref str i)))
-                     (n (cond ((< ch ?0)  nil)
-                              ((<= ch ?9) (- ch ?0))
-                              ((< ch ?A)  nil)
-                              ((<= ch ?Z) (- ch (- ?A 10)))
-                              (t          nil))))
-                (if (and n (<= 0 n) (< n radix))
-                  (+ n (* radix value))
-                  (progn
-                    (calculator-message
-                     "Warning: Ignoring bad input character `%c'." ch)
-                    (sit-for 1)
-                    value))))
-        (when (if (< new-value 0) (> value 0) (< value 0))
-          (calculator-message "Warning: Overflow in input."))
-        (setq value new-value))
-      value)
-    (car (read-from-string
-          (cond ((equal "." str) "0.0")
-                ((string-match-p "[eE][+-]?$" str) (concat str "0"))
-                ((string-match-p "\\.[0-9]\\|[eE]" str) str)
-                ((string-match-p "\\." str)
-                 ;; do this because Emacs reads "23." as an integer
-                 (concat str "0"))
-                ((stringp str) (concat str ".0"))
-                (t "0.0"))))))
+    (string-to-number str (cadr (assq calculator-input-radix
+                                      '((bin 2) (oct 8) (hex 16)))))
+    (let* ((str (replace-regexp-in-string
+                 "\\.\\([^0-9].*\\)?$" ".0\\1" str))
+           (str (replace-regexp-in-string
+                 "[eE][+-]?\\([^0-9].*\\)?$" "e0\\1" str)))
+      (string-to-number str))))
 
 (defun calculator-push-curnum ()
   "Push the numeric value of the displayed number to the stack."
@@ -1326,7 +1306,8 @@ Used with +/- for entering them as digits in numbers like 1e-3 (there is
 no need for negative numbers since these are handled by unary
 operators)."
   (interactive)
-  (if (and (not calculator-display-fragile)
+  (if (and (not calculator-input-radix)
+           (not calculator-display-fragile)
            calculator-curnum
            (string-match-p "[eE]$" calculator-curnum))
     (calculator-digit)
@@ -1494,25 +1475,27 @@ Used by `calculator-paste' and `get-register'."
              (or calculator-display-fragile
                  (not (numberp (car calculator-stack)))))
     (calculator-clear-fragile)
-    (setq calculator-curnum (let ((calculator-displayer "%S"))
-                              (calculator-number-to-string val)))
+    (setq calculator-curnum
+          (let ((calculator-displayer "%S")
+                (calculator-radix-grouping-mode nil)
+                (calculator-output-radix calculator-input-radix))
+            (calculator-number-to-string val)))
     (calculator-update-display)))
 
-(defun calculator-paste ()
-  "Paste a value from the `kill-ring'."
-  (interactive)
-  (calculator-put-value
-   (let ((str (replace-regexp-in-string
-               "^ *\\(.+[^ ]\\) *$" "\\1" (current-kill 0))))
-     (when (and (not calculator-input-radix)
-                calculator-paste-decimals
-                (string-match
-                 "\\([0-9]+\\)\\(\\.[0-9]+\\)?\\(e[0-9]+\\)?"
-                 str))
-       (setq str (concat (or (match-string 1 str) "0")
-                         (or (match-string 2 str) ".0")
-                         (or (match-string 3 str) ""))))
-     (ignore-errors (calculator-string-to-number str)))))
+(defun calculator-paste (arg)
+  "Paste a value from the `kill-ring'.
+
+With a prefix argument, paste the raw string as a sequence of key
+presses, which can be used to paste expressions.  Note that this
+is literal; examples: spaces will store values, pasting \"1+2\"
+will not produce 3 if it's done you're entering a number or after
+a multiplication."
+  (interactive "P")
+  (let ((str (current-kill 0)))
+    (if arg
+      (setq unread-command-events
+            `(,@(listify-key-sequence str) ,@unread-command-events))
+      (calculator-put-value (calculator-string-to-number str)))))
 
 (defun calculator-register-read-with-preview (prompt)
   "Similar to `register-read-with-preview' but for calculator
@@ -1555,8 +1538,8 @@ registers."
 * I inverse the next trig function     \
 * \\='/\"/{/}  - display/display args
 * D         - switch to all-decimal, or toggle deg/rad mode
-* B/O/H/X   - binary/octal/hex mode for i/o (X is a shortcut for H)
-* i/o       - prefix for d/b/o/x - set only input/output modes
+* B/O/H/X   - binary/octal/hex mode for i/o (both H and X are for hex)
+* i/o       - prefix for D/B/O/X - set only input/output modes
 * enter/=   - evaluate current expr.   * s/g      - set/get a register
 * space     - evaluate & save on list  * l/v      - list total/average
 * up/down/C-p/C-n - browse saved       * C-delete - clear all saved
@@ -1568,14 +1551,11 @@ registers."
   (if (eq last-command 'calculator-help)
     (let ((mode-name "Calculator")
           (major-mode 'calculator-mode)
-          (g-map (current-global-map))
           (win (selected-window)))
       (require 'ehelp)
       (if (not calculator-electric-mode)
         (describe-mode)
-        (progn (use-global-map calculator-saved-global-map)
-               (electric-describe-mode)
-               (use-global-map g-map)))
+        (electric-describe-mode))
       (select-window win)
       (message nil))
     (let ((one (one-window-p t))