diff options
| author | Andrea Corallo | 2020-05-24 10:20:23 +0100 |
|---|---|---|
| committer | Andrea Corallo | 2020-05-24 10:20:23 +0100 |
| commit | 9daffe9cfe82d3b1e1e9fa8929dbb40cfed60f0f (patch) | |
| tree | c9e78cbb4e151dc3c3996a65cf1eedab19248fb4 /lisp | |
| parent | f5dceed09a8234548d5b3acb76d443569533cab9 (diff) | |
| parent | e021c2dc2279e0fd3a5331f9ea661e4d39c2e840 (diff) | |
| download | emacs-9daffe9cfe82d3b1e1e9fa8929dbb40cfed60f0f.tar.gz emacs-9daffe9cfe82d3b1e1e9fa8929dbb40cfed60f0f.zip | |
Merge remote-tracking branch 'savannah/master' into HEAD
Diffstat (limited to 'lisp')
32 files changed, 738 insertions, 848 deletions
diff --git a/lisp/calculator.el b/lisp/calculator.el index 7e0b2fcc6a3..cd92f992689 100644 --- a/lisp/calculator.el +++ b/lisp/calculator.el | |||
| @@ -858,12 +858,10 @@ The result should not exceed the screen width." | |||
| 858 | "Convert the given STR to a number, according to the value of | 858 | "Convert the given STR to a number, according to the value of |
| 859 | `calculator-input-radix'." | 859 | `calculator-input-radix'." |
| 860 | (if calculator-input-radix | 860 | (if calculator-input-radix |
| 861 | (string-to-number str (cadr (assq calculator-input-radix | 861 | (string-to-number str (cadr (assq calculator-input-radix |
| 862 | '((bin 2) (oct 8) (hex 16))))) | 862 | '((bin 2) (oct 8) (hex 16))))) |
| 863 | (let* ((str (replace-regexp-in-string | 863 | ;; Allow entry of "1.e3". |
| 864 | "\\.\\([^0-9].*\\)?$" ".0\\1" str)) | 864 | (let ((str (replace-regexp-in-string (rx "." (any "eE")) "e" str))) |
| 865 | (str (replace-regexp-in-string | ||
| 866 | "[eE][+-]?\\([^0-9].*\\)?$" "e0\\1" str))) | ||
| 867 | (float (string-to-number str))))) | 865 | (float (string-to-number str))))) |
| 868 | 866 | ||
| 869 | (defun calculator-push-curnum () | 867 | (defun calculator-push-curnum () |
diff --git a/lisp/cedet/ede.el b/lisp/cedet/ede.el index 8c336117c92..41252815734 100644 --- a/lisp/cedet/ede.el +++ b/lisp/cedet/ede.el | |||
| @@ -1515,8 +1515,11 @@ It does not apply the value to buffers." | |||
| 1515 | (when project-dir | 1515 | (when project-dir |
| 1516 | (ede-directory-get-open-project project-dir 'ROOT)))) | 1516 | (ede-directory-get-open-project project-dir 'ROOT)))) |
| 1517 | 1517 | ||
| 1518 | (cl-defmethod project-roots ((project ede-project)) | 1518 | (cl-defmethod project-root ((project ede-project)) |
| 1519 | (list (ede-project-root-directory project))) | 1519 | (ede-project-root-directory project)) |
| 1520 | |||
| 1521 | ;;; FIXME: Could someone look into implementing `project-ignores' for | ||
| 1522 | ;;; EDE and/or a faster `project-files'? | ||
| 1520 | 1523 | ||
| 1521 | (add-hook 'project-find-functions #'project-try-ede) | 1524 | (add-hook 'project-find-functions #'project-try-ede) |
| 1522 | 1525 | ||
diff --git a/lisp/doc-view.el b/lisp/doc-view.el index 3cac2629a9c..de342f1519e 100644 --- a/lisp/doc-view.el +++ b/lisp/doc-view.el | |||
| @@ -2050,8 +2050,8 @@ See the command `doc-view-mode' for more information on this mode." | |||
| 2050 | (when (memq (selected-frame) (alist-get 'frames attrs)) | 2050 | (when (memq (selected-frame) (alist-get 'frames attrs)) |
| 2051 | (let ((geom (alist-get 'geometry attrs))) | 2051 | (let ((geom (alist-get 'geometry attrs))) |
| 2052 | (when geom | 2052 | (when geom |
| 2053 | (setq monitor-top (nth 0 geom)) | 2053 | (setq monitor-left (nth 0 geom)) |
| 2054 | (setq monitor-left (nth 1 geom)) | 2054 | (setq monitor-top (nth 1 geom)) |
| 2055 | (setq monitor-width (nth 2 geom)) | 2055 | (setq monitor-width (nth 2 geom)) |
| 2056 | (setq monitor-height (nth 3 geom)))))) | 2056 | (setq monitor-height (nth 3 geom)))))) |
| 2057 | (let ((frame (make-frame | 2057 | (let ((frame (make-frame |
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el index 95659840ad5..808e4f34fc5 100644 --- a/lisp/emacs-lisp/package.el +++ b/lisp/emacs-lisp/package.el | |||
| @@ -397,6 +397,26 @@ synchronously." | |||
| 397 | :type 'boolean | 397 | :type 'boolean |
| 398 | :version "25.1") | 398 | :version "25.1") |
| 399 | 399 | ||
| 400 | (defcustom package-name-column-width 30 | ||
| 401 | "Column width for the Package name in the package menu." | ||
| 402 | :type 'number | ||
| 403 | :version "28.1") | ||
| 404 | |||
| 405 | (defcustom package-version-column-width 14 | ||
| 406 | "Column width for the Package version in the package menu." | ||
| 407 | :type 'number | ||
| 408 | :version "28.1") | ||
| 409 | |||
| 410 | (defcustom package-status-column-width 12 | ||
| 411 | "Column width for the Package status in the package menu." | ||
| 412 | :type 'number | ||
| 413 | :version "28.1") | ||
| 414 | |||
| 415 | (defcustom package-archive-column-width 8 | ||
| 416 | "Column width for the Package status in the package menu." | ||
| 417 | :type 'number | ||
| 418 | :version "28.1") | ||
| 419 | |||
| 400 | 420 | ||
| 401 | ;;; `package-desc' object definition | 421 | ;;; `package-desc' object definition |
| 402 | ;; This is the struct used internally to represent packages. | 422 | ;; This is the struct used internally to represent packages. |
| @@ -2750,11 +2770,11 @@ Letters do not insert themselves; instead, they are commands. | |||
| 2750 | (package-menu--transaction-status | 2770 | (package-menu--transaction-status |
| 2751 | package-menu--transaction-status))) | 2771 | package-menu--transaction-status))) |
| 2752 | (setq tabulated-list-format | 2772 | (setq tabulated-list-format |
| 2753 | `[("Package" 18 package-menu--name-predicate) | 2773 | `[("Package" ,package-name-column-width package-menu--name-predicate) |
| 2754 | ("Version" 13 package-menu--version-predicate) | 2774 | ("Version" ,package-version-column-width package-menu--version-predicate) |
| 2755 | ("Status" 10 package-menu--status-predicate) | 2775 | ("Status" ,package-status-column-width package-menu--status-predicate) |
| 2756 | ,@(if (cdr package-archives) | 2776 | ,@(if (cdr package-archives) |
| 2757 | '(("Archive" 10 package-menu--archive-predicate))) | 2777 | `(("Archive" ,package-archive-column-width package-menu--archive-predicate))) |
| 2758 | ("Description" 0 package-menu--description-predicate)]) | 2778 | ("Description" 0 package-menu--description-predicate)]) |
| 2759 | (setq tabulated-list-padding 2) | 2779 | (setq tabulated-list-padding 2) |
| 2760 | (setq tabulated-list-sort-key (cons "Status" nil)) | 2780 | (setq tabulated-list-sort-key (cons "Status" nil)) |
diff --git a/lisp/emacs-lisp/syntax.el b/lisp/emacs-lisp/syntax.el index 46dc8d9ade8..ce495af95bc 100644 --- a/lisp/emacs-lisp/syntax.el +++ b/lisp/emacs-lisp/syntax.el | |||
| @@ -139,14 +139,28 @@ delimiter or an Escaped or Char-quoted character.")) | |||
| 139 | (point-max)))) | 139 | (point-max)))) |
| 140 | (cons beg end)) | 140 | (cons beg end)) |
| 141 | 141 | ||
| 142 | (defun syntax-propertize--shift-groups (re n) | 142 | (defun syntax-propertize--shift-groups-and-backrefs (re n) |
| 143 | (replace-regexp-in-string | 143 | (let ((new-re (replace-regexp-in-string |
| 144 | "\\\\(\\?\\([0-9]+\\):" | 144 | "\\\\(\\?\\([0-9]+\\):" |
| 145 | (lambda (s) | 145 | (lambda (s) |
| 146 | (replace-match | 146 | (replace-match |
| 147 | (number-to-string (+ n (string-to-number (match-string 1 s)))) | 147 | (number-to-string |
| 148 | t t s 1)) | 148 | (+ n (string-to-number (match-string 1 s)))) |
| 149 | re t t)) | 149 | t t s 1)) |
| 150 | re t t)) | ||
| 151 | (pos 0)) | ||
| 152 | (while (string-match "\\\\\\([0-9]+\\)" new-re pos) | ||
| 153 | (setq pos (+ 1 (match-beginning 1))) | ||
| 154 | (when (save-match-data | ||
| 155 | ;; With \N, the \ must be in a subregexp context, i.e., | ||
| 156 | ;; not in a character class or in a \{\} repetition. | ||
| 157 | (subregexp-context-p new-re (match-beginning 0))) | ||
| 158 | (let ((shifted (+ n (string-to-number (match-string 1 new-re))))) | ||
| 159 | (when (> shifted 9) | ||
| 160 | (error "There may be at most nine back-references")) | ||
| 161 | (setq new-re (replace-match (number-to-string shifted) | ||
| 162 | t t new-re 1))))) | ||
| 163 | new-re)) | ||
| 150 | 164 | ||
| 151 | (defmacro syntax-propertize-precompile-rules (&rest rules) | 165 | (defmacro syntax-propertize-precompile-rules (&rest rules) |
| 152 | "Return a precompiled form of RULES to pass to `syntax-propertize-rules'. | 166 | "Return a precompiled form of RULES to pass to `syntax-propertize-rules'. |
| @@ -190,7 +204,8 @@ for subsequent HIGHLIGHTs. | |||
| 190 | Also SYNTAX is free to move point, in which case RULES may not be applied to | 204 | Also SYNTAX is free to move point, in which case RULES may not be applied to |
| 191 | some parts of the text or may be applied several times to other parts. | 205 | some parts of the text or may be applied several times to other parts. |
| 192 | 206 | ||
| 193 | Note: back-references in REGEXPs do not work." | 207 | Note: There may be at most nine back-references in the REGEXPs of |
| 208 | all RULES in total." | ||
| 194 | (declare (debug (&rest &or symbolp ;FIXME: edebug this eval step. | 209 | (declare (debug (&rest &or symbolp ;FIXME: edebug this eval step. |
| 195 | (form &rest | 210 | (form &rest |
| 196 | (numberp | 211 | (numberp |
| @@ -219,7 +234,7 @@ Note: back-references in REGEXPs do not work." | |||
| 219 | ;; tell when *this* match 0 has succeeded. | 234 | ;; tell when *this* match 0 has succeeded. |
| 220 | (cl-incf offset) | 235 | (cl-incf offset) |
| 221 | (setq re (concat "\\(" re "\\)"))) | 236 | (setq re (concat "\\(" re "\\)"))) |
| 222 | (setq re (syntax-propertize--shift-groups re offset)) | 237 | (setq re (syntax-propertize--shift-groups-and-backrefs re offset)) |
| 223 | (let ((code '()) | 238 | (let ((code '()) |
| 224 | (condition | 239 | (condition |
| 225 | (cond | 240 | (cond |
diff --git a/lisp/gnus/gnus-art.el b/lisp/gnus/gnus-art.el index 6b9610d3121..614651afff9 100644 --- a/lisp/gnus/gnus-art.el +++ b/lisp/gnus/gnus-art.el | |||
| @@ -5833,6 +5833,7 @@ all parts." | |||
| 5833 | "" "...")) | 5833 | "" "...")) |
| 5834 | (gnus-tmp-length (with-current-buffer (mm-handle-buffer handle) | 5834 | (gnus-tmp-length (with-current-buffer (mm-handle-buffer handle) |
| 5835 | (buffer-size))) | 5835 | (buffer-size))) |
| 5836 | (help-echo "mouse-2: toggle the MIME part; down-mouse-3: more options") | ||
| 5836 | gnus-tmp-type-long b e) | 5837 | gnus-tmp-type-long b e) |
| 5837 | (when (string-match ".*/" gnus-tmp-name) | 5838 | (when (string-match ".*/" gnus-tmp-name) |
| 5838 | (setq gnus-tmp-name (replace-match "" t t gnus-tmp-name))) | 5839 | (setq gnus-tmp-name (replace-match "" t t gnus-tmp-name))) |
| @@ -5841,6 +5842,16 @@ all parts." | |||
| 5841 | (concat "; " gnus-tmp-name)))) | 5842 | (concat "; " gnus-tmp-name)))) |
| 5842 | (unless (equal gnus-tmp-description "") | 5843 | (unless (equal gnus-tmp-description "") |
| 5843 | (setq gnus-tmp-type-long (concat " --- " gnus-tmp-type-long))) | 5844 | (setq gnus-tmp-type-long (concat " --- " gnus-tmp-type-long))) |
| 5845 | (when (zerop gnus-tmp-length) | ||
| 5846 | (setq gnus-tmp-type-long | ||
| 5847 | (concat | ||
| 5848 | gnus-tmp-type-long | ||
| 5849 | (substitute-command-keys | ||
| 5850 | (concat "\\<gnus-summary-mode-map> (not downloaded, " | ||
| 5851 | "\\[gnus-summary-show-complete-article] to fetch.)")))) | ||
| 5852 | (setq help-echo | ||
| 5853 | (concat "Type \\[gnus-summary-show-complete-article] " | ||
| 5854 | "to download complete article. " help-echo))) | ||
| 5844 | (setq b (point)) | 5855 | (setq b (point)) |
| 5845 | (gnus-eval-format | 5856 | (gnus-eval-format |
| 5846 | gnus-mime-button-line-format gnus-mime-button-line-format-alist | 5857 | gnus-mime-button-line-format gnus-mime-button-line-format-alist |
| @@ -5859,8 +5870,7 @@ all parts." | |||
| 5859 | 'keymap gnus-mime-button-map | 5870 | 'keymap gnus-mime-button-map |
| 5860 | 'face gnus-article-button-face | 5871 | 'face gnus-article-button-face |
| 5861 | 'follow-link t | 5872 | 'follow-link t |
| 5862 | 'help-echo | 5873 | 'help-echo help-echo))) |
| 5863 | "mouse-2: toggle the MIME part; down-mouse-3: more options"))) | ||
| 5864 | 5874 | ||
| 5865 | (defvar gnus-displaying-mime nil) | 5875 | (defvar gnus-displaying-mime nil) |
| 5866 | 5876 | ||
diff --git a/lisp/gnus/gnus-registry.el b/lisp/gnus/gnus-registry.el index 480ed80ef81..f306889a7fc 100644 --- a/lisp/gnus/gnus-registry.el +++ b/lisp/gnus/gnus-registry.el | |||
| @@ -485,23 +485,25 @@ This is not required after changing `gnus-registry-cache-file'." | |||
| 485 | (when from | 485 | (when from |
| 486 | (setq entry (cons (delete from (assoc 'group entry)) | 486 | (setq entry (cons (delete from (assoc 'group entry)) |
| 487 | (assq-delete-all 'group entry)))) | 487 | (assq-delete-all 'group entry)))) |
| 488 | 488 | ;; Only keep the entry if the message is going to a new group, or | |
| 489 | (dolist (kv `((group ,to) | 489 | ;; it's still in some previous group. |
| 490 | (sender ,sender) | 490 | (when (or to (alist-get 'group entry)) |
| 491 | (recipient ,@recipients) | 491 | (dolist (kv `((group ,to) |
| 492 | (subject ,subject))) | 492 | (sender ,sender) |
| 493 | (when (cadr kv) | 493 | (recipient ,@recipients) |
| 494 | (let ((new (or (assq (car kv) entry) | 494 | (subject ,subject))) |
| 495 | (list (car kv))))) | 495 | (when (cadr kv) |
| 496 | (dolist (toadd (cdr kv)) | 496 | (let ((new (or (assq (car kv) entry) |
| 497 | (unless (member toadd new) | 497 | (list (car kv))))) |
| 498 | (setq new (append new (list toadd))))) | 498 | (dolist (toadd (cdr kv)) |
| 499 | (setq entry (cons new | 499 | (unless (member toadd new) |
| 500 | (assq-delete-all (car kv) entry)))))) | 500 | (setq new (append new (list toadd))))) |
| 501 | (gnus-message 10 "Gnus registry: new entry for %s is %S" | 501 | (setq entry (cons new |
| 502 | id | 502 | (assq-delete-all (car kv) entry)))))) |
| 503 | entry) | 503 | (gnus-message 10 "Gnus registry: new entry for %s is %S" |
| 504 | (gnus-registry-insert db id entry))) | 504 | id |
| 505 | entry) | ||
| 506 | (gnus-registry-insert db id entry)))) | ||
| 505 | 507 | ||
| 506 | ;; Function for nn{mail|imap}-split-fancy: look up all references in | 508 | ;; Function for nn{mail|imap}-split-fancy: look up all references in |
| 507 | ;; the cache and if a match is found, return that group. | 509 | ;; the cache and if a match is found, return that group. |
diff --git a/lisp/ido.el b/lisp/ido.el index 81883402add..ad71d468cb4 100644 --- a/lisp/ido.el +++ b/lisp/ido.el | |||
| @@ -499,11 +499,14 @@ This means that \\[ido-complete] must always be followed by \\[ido-exit-minibuff | |||
| 499 | even when there is only one unique completion." | 499 | even when there is only one unique completion." |
| 500 | :type 'boolean) | 500 | :type 'boolean) |
| 501 | 501 | ||
| 502 | (defcustom ido-cannot-complete-command 'ido-completion-help | 502 | (defcustom ido-cannot-complete-command #'ido-completion-auto-help |
| 503 | "Command run when `ido-complete' can't complete any more. | 503 | "Command run when `ido-complete' can't complete any more. |
| 504 | The most useful values are `ido-completion-help', which pops up a | 504 | The most useful values are `ido-completion-help', which pops up a |
| 505 | window with completion alternatives, or `ido-next-match' or | 505 | window with completion alternatives; `ido-completion-auto-help', |
| 506 | `ido-prev-match', which cycle the buffer list." | 506 | which does the same but respects the value of |
| 507 | `completion-auto-help'; and `ido-next-match' or `ido-prev-match', | ||
| 508 | which cycle the buffer list." | ||
| 509 | :version "28.1" | ||
| 507 | :type 'function) | 510 | :type 'function) |
| 508 | 511 | ||
| 509 | 512 | ||
| @@ -1546,7 +1549,7 @@ This function also adds a hook to the minibuffer." | |||
| 1546 | ((> (prefix-numeric-value arg) 0) 'both) | 1549 | ((> (prefix-numeric-value arg) 0) 'both) |
| 1547 | (t nil))) | 1550 | (t nil))) |
| 1548 | 1551 | ||
| 1549 | (ido-everywhere (if ido-everywhere 1 -1)) | 1552 | (ido-everywhere (if (and ido-mode ido-everywhere) 1 -1)) |
| 1550 | 1553 | ||
| 1551 | (when ido-mode | 1554 | (when ido-mode |
| 1552 | (ido-common-initialization) | 1555 | (ido-common-initialization) |
| @@ -3926,6 +3929,14 @@ If `ido-change-word-sub' cannot be found in WORD, return nil." | |||
| 3926 | (when (bobp) | 3929 | (when (bobp) |
| 3927 | (next-completion 1))))) | 3930 | (next-completion 1))))) |
| 3928 | 3931 | ||
| 3932 | (defun ido-completion-auto-help () | ||
| 3933 | "Call `ido-completion-help' if `completion-auto-help' is non-nil." | ||
| 3934 | (interactive) | ||
| 3935 | ;; Note: `completion-auto-help' could also be `lazy', but this value | ||
| 3936 | ;; is irrelevant to ido, which is fundamentally eager, so it is | ||
| 3937 | ;; treated the same as t. | ||
| 3938 | (when completion-auto-help | ||
| 3939 | (ido-completion-help))) | ||
| 3929 | 3940 | ||
| 3930 | (defun ido-completion-help () | 3941 | (defun ido-completion-help () |
| 3931 | "Show possible completions in the `ido-completion-buffer'." | 3942 | "Show possible completions in the `ido-completion-buffer'." |
diff --git a/lisp/json.el b/lisp/json.el index 6f3b791ed17..9002e868537 100644 --- a/lisp/json.el +++ b/lisp/json.el | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | ;; Copyright (C) 2006-2020 Free Software Foundation, Inc. | 3 | ;; Copyright (C) 2006-2020 Free Software Foundation, Inc. |
| 4 | 4 | ||
| 5 | ;; Author: Theresa O'Connor <ted@oconnor.cx> | 5 | ;; Author: Theresa O'Connor <ted@oconnor.cx> |
| 6 | ;; Version: 1.4 | 6 | ;; Version: 1.5 |
| 7 | ;; Keywords: convenience | 7 | ;; Keywords: convenience |
| 8 | 8 | ||
| 9 | ;; This file is part of GNU Emacs. | 9 | ;; This file is part of GNU Emacs. |
| @@ -29,11 +29,11 @@ | |||
| 29 | ;; Learn all about JSON here: <URL:http://json.org/>. | 29 | ;; Learn all about JSON here: <URL:http://json.org/>. |
| 30 | 30 | ||
| 31 | ;; The user-serviceable entry points for the parser are the functions | 31 | ;; The user-serviceable entry points for the parser are the functions |
| 32 | ;; `json-read' and `json-read-from-string'. The encoder has a single | 32 | ;; `json-read' and `json-read-from-string'. The encoder has a single |
| 33 | ;; entry point, `json-encode'. | 33 | ;; entry point, `json-encode'. |
| 34 | 34 | ||
| 35 | ;; Since there are several natural representations of key-value pair | 35 | ;; Since there are several natural representations of key-value pair |
| 36 | ;; mappings in elisp (alist, plist, hash-table), `json-read' allows you | 36 | ;; mappings in Elisp (alist, plist, hash-table), `json-read' allows you |
| 37 | ;; to specify which you'd prefer (see `json-object-type' and | 37 | ;; to specify which you'd prefer (see `json-object-type' and |
| 38 | ;; `json-array-type'). | 38 | ;; `json-array-type'). |
| 39 | 39 | ||
| @@ -55,6 +55,7 @@ | |||
| 55 | ;;; Code: | 55 | ;;; Code: |
| 56 | 56 | ||
| 57 | (require 'map) | 57 | (require 'map) |
| 58 | (require 'seq) | ||
| 58 | (require 'subr-x) | 59 | (require 'subr-x) |
| 59 | 60 | ||
| 60 | ;; Parameters | 61 | ;; Parameters |
| @@ -113,8 +114,10 @@ Used only when `json-encoding-pretty-print' is non-nil.") | |||
| 113 | "If non-nil, then the output of `json-encode' will be pretty-printed.") | 114 | "If non-nil, then the output of `json-encode' will be pretty-printed.") |
| 114 | 115 | ||
| 115 | (defvar json-encoding-lisp-style-closings nil | 116 | (defvar json-encoding-lisp-style-closings nil |
| 116 | "If non-nil, ] and } closings will be formatted lisp-style, | 117 | "If non-nil, delimiters ] and } will be formatted Lisp-style. |
| 117 | without indentation.") | 118 | This means they will be placed on the same line as the last |
| 119 | element of the respective array or object, without indentation. | ||
| 120 | Used only when `json-encoding-pretty-print' is non-nil.") | ||
| 118 | 121 | ||
| 119 | (defvar json-encoding-object-sort-predicate nil | 122 | (defvar json-encoding-object-sort-predicate nil |
| 120 | "Sorting predicate for JSON object keys during encoding. | 123 | "Sorting predicate for JSON object keys during encoding. |
| @@ -124,88 +127,81 @@ instance, setting this to `string<' will have JSON object keys | |||
| 124 | ordered alphabetically.") | 127 | ordered alphabetically.") |
| 125 | 128 | ||
| 126 | (defvar json-pre-element-read-function nil | 129 | (defvar json-pre-element-read-function nil |
| 127 | "Function called (if non-nil) by `json-read-array' and | 130 | "If non-nil, a function to call before reading a JSON array or object. |
| 128 | `json-read-object' right before reading a JSON array or object, | 131 | It is called by `json-read-array' and `json-read-object', |
| 129 | respectively. The function is called with one argument, which is | 132 | respectively, with one argument, which is the current JSON key.") |
| 130 | the current JSON key.") | ||
| 131 | 133 | ||
| 132 | (defvar json-post-element-read-function nil | 134 | (defvar json-post-element-read-function nil |
| 133 | "Function called (if non-nil) by `json-read-array' and | 135 | "If non-nil, a function to call after reading a JSON array or object. |
| 134 | `json-read-object' right after reading a JSON array or object, | 136 | It is called by `json-read-array' and `json-read-object', |
| 135 | respectively.") | 137 | respectively, with no arguments.") |
| 136 | 138 | ||
| 137 | 139 | ||
| 138 | 140 | ||
| 139 | ;;; Utilities | 141 | ;;; Utilities |
| 140 | 142 | ||
| 141 | (defun json-join (strings separator) | 143 | (define-obsolete-function-alias 'json-join #'string-join "28.1") |
| 142 | "Join STRINGS with SEPARATOR." | ||
| 143 | (mapconcat 'identity strings separator)) | ||
| 144 | 144 | ||
| 145 | (defun json-alist-p (list) | 145 | (defun json-alist-p (list) |
| 146 | "Non-null if and only if LIST is an alist with simple keys." | 146 | "Non-nil if and only if LIST is an alist with simple keys." |
| 147 | (while (consp list) | 147 | (declare (pure t) (side-effect-free error-free)) |
| 148 | (setq list (if (and (consp (car list)) | 148 | (while (and (consp (car-safe list)) |
| 149 | (atom (caar list))) | 149 | (atom (caar list)) |
| 150 | (cdr list) | 150 | (setq list (cdr list)))) |
| 151 | 'not-alist))) | ||
| 152 | (null list)) | 151 | (null list)) |
| 153 | 152 | ||
| 154 | (defun json-plist-p (list) | 153 | (defun json-plist-p (list) |
| 155 | "Non-null if and only if LIST is a plist with keyword keys." | 154 | "Non-nil if and only if LIST is a plist with keyword keys." |
| 156 | (while (consp list) | 155 | (declare (pure t) (side-effect-free error-free)) |
| 157 | (setq list (if (and (keywordp (car list)) | 156 | (while (and (keywordp (car-safe list)) |
| 158 | (consp (cdr list))) | 157 | (consp (cdr list)) |
| 159 | (cddr list) | 158 | (setq list (cddr list)))) |
| 160 | 'not-plist))) | ||
| 161 | (null list)) | 159 | (null list)) |
| 162 | 160 | ||
| 163 | (defun json--plist-reverse (plist) | 161 | (defun json--plist-nreverse (plist) |
| 164 | "Return a copy of PLIST in reverse order. | 162 | "Return PLIST in reverse order. |
| 165 | Unlike `reverse', this keeps the property-value pairs intact." | 163 | Unlike `nreverse', this keeps the ordering of each property |
| 166 | (let (res) | 164 | relative to its value intact. Like `nreverse', this function may |
| 167 | (while plist | 165 | destructively modify PLIST to produce the result." |
| 168 | (let ((prop (pop plist)) | 166 | (let (prev (next (cddr plist))) |
| 169 | (val (pop plist))) | 167 | (while next |
| 170 | (push val res) | 168 | (setcdr (cdr plist) prev) |
| 171 | (push prop res))) | 169 | (setq prev plist plist next next (cddr next)) |
| 172 | res)) | 170 | (setcdr (cdr plist) prev))) |
| 173 | 171 | plist) | |
| 174 | (defun json--plist-to-alist (plist) | 172 | |
| 175 | "Return an alist of the property-value pairs in PLIST." | 173 | (defmacro json--with-indentation (&rest body) |
| 176 | (let (res) | 174 | "Evaluate BODY with the correct indentation for JSON encoding. |
| 177 | (while plist | 175 | This macro binds `json--encoding-current-indentation' according |
| 178 | (let ((prop (pop plist)) | 176 | to `json-encoding-pretty-print' around BODY." |
| 179 | (val (pop plist))) | 177 | (declare (debug t) (indent 0)) |
| 180 | (push (cons prop val) res))) | ||
| 181 | (nreverse res))) | ||
| 182 | |||
| 183 | (defmacro json--with-indentation (body) | ||
| 184 | `(let ((json--encoding-current-indentation | 178 | `(let ((json--encoding-current-indentation |
| 185 | (if json-encoding-pretty-print | 179 | (if json-encoding-pretty-print |
| 186 | (concat json--encoding-current-indentation | 180 | (concat json--encoding-current-indentation |
| 187 | json-encoding-default-indentation) | 181 | json-encoding-default-indentation) |
| 188 | ""))) | 182 | ""))) |
| 189 | ,body)) | 183 | ,@body)) |
| 190 | 184 | ||
| 191 | ;; Reader utilities | 185 | ;; Reader utilities |
| 192 | 186 | ||
| 193 | (define-inline json-advance (&optional n) | 187 | (define-inline json-advance (&optional n) |
| 194 | "Advance N characters forward." | 188 | "Advance N characters forward, or 1 character if N is nil. |
| 189 | On reaching the end of the accessible region of the buffer, stop | ||
| 190 | and signal an error." | ||
| 195 | (inline-quote (forward-char ,n))) | 191 | (inline-quote (forward-char ,n))) |
| 196 | 192 | ||
| 197 | (define-inline json-peek () | 193 | (define-inline json-peek () |
| 198 | "Return the character at point." | 194 | "Return the character at point. |
| 195 | At the end of the accessible region of the buffer, return 0." | ||
| 199 | (inline-quote (following-char))) | 196 | (inline-quote (following-char))) |
| 200 | 197 | ||
| 201 | (define-inline json-pop () | 198 | (define-inline json-pop () |
| 202 | "Advance past the character at point, returning it." | 199 | "Advance past the character at point, returning it. |
| 200 | Signal `json-end-of-file' if called at the end of the buffer." | ||
| 203 | (inline-quote | 201 | (inline-quote |
| 204 | (let ((char (json-peek))) | 202 | (prog1 (or (char-after) |
| 205 | (if (zerop char) | 203 | (signal 'json-end-of-file ())) |
| 206 | (signal 'json-end-of-file nil) | 204 | (json-advance)))) |
| 207 | (json-advance) | ||
| 208 | char)))) | ||
| 209 | 205 | ||
| 210 | (define-inline json-skip-whitespace () | 206 | (define-inline json-skip-whitespace () |
| 211 | "Skip past the whitespace at point." | 207 | "Skip past the whitespace at point." |
| @@ -213,7 +209,7 @@ Unlike `reverse', this keeps the property-value pairs intact." | |||
| 213 | ;; https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf | 209 | ;; https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf |
| 214 | ;; or https://tools.ietf.org/html/rfc7159#section-2 for the | 210 | ;; or https://tools.ietf.org/html/rfc7159#section-2 for the |
| 215 | ;; definition of whitespace in JSON. | 211 | ;; definition of whitespace in JSON. |
| 216 | (inline-quote (skip-chars-forward "\t\r\n "))) | 212 | (inline-quote (skip-chars-forward "\t\n\r "))) |
| 217 | 213 | ||
| 218 | 214 | ||
| 219 | 215 | ||
| @@ -236,8 +232,8 @@ Unlike `reverse', this keeps the property-value pairs intact." | |||
| 236 | ;;; Paths | 232 | ;;; Paths |
| 237 | 233 | ||
| 238 | (defvar json--path '() | 234 | (defvar json--path '() |
| 239 | "Used internally by `json-path-to-position' to keep track of | 235 | "Keeps track of the path during recursive calls to `json-read'. |
| 240 | the path during recursive calls to `json-read'.") | 236 | Used internally by `json-path-to-position'.") |
| 241 | 237 | ||
| 242 | (defun json--record-path (key) | 238 | (defun json--record-path (key) |
| 243 | "Record the KEY to the current JSON path. | 239 | "Record the KEY to the current JSON path. |
| @@ -248,7 +244,7 @@ Used internally by `json-path-to-position'." | |||
| 248 | "Check if the last parsed JSON structure passed POSITION. | 244 | "Check if the last parsed JSON structure passed POSITION. |
| 249 | Used internally by `json-path-to-position'." | 245 | Used internally by `json-path-to-position'." |
| 250 | (let ((start (caar json--path))) | 246 | (let ((start (caar json--path))) |
| 251 | (when (< start position (+ (point) 1)) | 247 | (when (< start position (1+ (point))) |
| 252 | (throw :json-path (list :path (nreverse (mapcar #'cdr json--path)) | 248 | (throw :json-path (list :path (nreverse (mapcar #'cdr json--path)) |
| 253 | :match-start start | 249 | :match-start start |
| 254 | :match-end (point))))) | 250 | :match-end (point))))) |
| @@ -266,13 +262,13 @@ properties: | |||
| 266 | :path -- A list of strings and numbers forming the path to | 262 | :path -- A list of strings and numbers forming the path to |
| 267 | the JSON element at the given position. Strings | 263 | the JSON element at the given position. Strings |
| 268 | denote object names, while numbers denote array | 264 | denote object names, while numbers denote array |
| 269 | indexes. | 265 | indices. |
| 270 | 266 | ||
| 271 | :match-start -- Position where the matched JSON element begins. | 267 | :match-start -- Position where the matched JSON element begins. |
| 272 | 268 | ||
| 273 | :match-end -- Position where the matched JSON element ends. | 269 | :match-end -- Position where the matched JSON element ends. |
| 274 | 270 | ||
| 275 | This can for instance be useful to determine the path to a JSON | 271 | This can, for instance, be useful to determine the path to a JSON |
| 276 | element in a deeply nested structure." | 272 | element in a deeply nested structure." |
| 277 | (save-excursion | 273 | (save-excursion |
| 278 | (unless string | 274 | (unless string |
| @@ -280,7 +276,7 @@ element in a deeply nested structure." | |||
| 280 | (let* ((json--path '()) | 276 | (let* ((json--path '()) |
| 281 | (json-pre-element-read-function #'json--record-path) | 277 | (json-pre-element-read-function #'json--record-path) |
| 282 | (json-post-element-read-function | 278 | (json-post-element-read-function |
| 283 | (apply-partially #'json--check-position position)) | 279 | (lambda () (json--check-position position))) |
| 284 | (path (catch :json-path | 280 | (path (catch :json-path |
| 285 | (if string | 281 | (if string |
| 286 | (json-read-from-string string) | 282 | (json-read-from-string string) |
| @@ -290,38 +286,33 @@ element in a deeply nested structure." | |||
| 290 | 286 | ||
| 291 | ;;; Keywords | 287 | ;;; Keywords |
| 292 | 288 | ||
| 293 | (defvar json-keywords '("true" "false" "null") | 289 | (defconst json-keywords '("true" "false" "null") |
| 294 | "List of JSON keywords.") | 290 | "List of JSON keywords.") |
| 291 | (make-obsolete-variable 'json-keywords "it is no longer used." "28.1") | ||
| 295 | 292 | ||
| 296 | ;; Keyword parsing | 293 | ;; Keyword parsing |
| 297 | 294 | ||
| 295 | ;; Characters that can follow a JSON value. | ||
| 296 | (rx-define json--post-value (| (in "\t\n\r ,]}") eos)) | ||
| 297 | |||
| 298 | (defun json-read-keyword (keyword) | 298 | (defun json-read-keyword (keyword) |
| 299 | "Read a JSON keyword at point. | 299 | "Read the expected JSON KEYWORD at point." |
| 300 | KEYWORD is the keyword expected." | 300 | (prog1 (cond ((equal keyword "true") t) |
| 301 | (unless (member keyword json-keywords) | 301 | ((equal keyword "false") json-false) |
| 302 | (signal 'json-unknown-keyword (list keyword))) | 302 | ((equal keyword "null") json-null) |
| 303 | (mapc (lambda (char) | 303 | (t (signal 'json-unknown-keyword (list keyword)))) |
| 304 | (when (/= char (json-peek)) | 304 | (or (looking-at-p keyword) |
| 305 | (signal 'json-unknown-keyword | 305 | (signal 'json-unknown-keyword (list (thing-at-point 'word)))) |
| 306 | (list (save-excursion | 306 | (json-advance (length keyword)) |
| 307 | (backward-word-strictly 1) | 307 | (or (looking-at-p (rx json--post-value)) |
| 308 | (thing-at-point 'word))))) | 308 | (signal 'json-unknown-keyword (list (thing-at-point 'word)))) |
| 309 | (json-advance)) | 309 | (json-skip-whitespace))) |
| 310 | keyword) | ||
| 311 | (json-skip-whitespace) | ||
| 312 | (unless (looking-at "\\([],}]\\|$\\)") | ||
| 313 | (signal 'json-unknown-keyword | ||
| 314 | (list (save-excursion | ||
| 315 | (backward-word-strictly 1) | ||
| 316 | (thing-at-point 'word))))) | ||
| 317 | (cond ((string-equal keyword "true") t) | ||
| 318 | ((string-equal keyword "false") json-false) | ||
| 319 | ((string-equal keyword "null") json-null))) | ||
| 320 | 310 | ||
| 321 | ;; Keyword encoding | 311 | ;; Keyword encoding |
| 322 | 312 | ||
| 323 | (defun json-encode-keyword (keyword) | 313 | (defun json-encode-keyword (keyword) |
| 324 | "Encode KEYWORD as a JSON value." | 314 | "Encode KEYWORD as a JSON value." |
| 315 | (declare (side-effect-free t)) | ||
| 325 | (cond ((eq keyword t) "true") | 316 | (cond ((eq keyword t) "true") |
| 326 | ((eq keyword json-false) "false") | 317 | ((eq keyword json-false) "false") |
| 327 | ((eq keyword json-null) "null"))) | 318 | ((eq keyword json-null) "null"))) |
| @@ -330,37 +321,31 @@ KEYWORD is the keyword expected." | |||
| 330 | 321 | ||
| 331 | ;; Number parsing | 322 | ;; Number parsing |
| 332 | 323 | ||
| 333 | (defun json-read-number (&optional sign) | 324 | (rx-define json--number |
| 334 | "Read the JSON number following point. | 325 | (: (? ?-) ; Sign. |
| 335 | The optional SIGN argument is for internal use. | 326 | (| (: (in "1-9") (* digit)) ?0) ; Integer. |
| 336 | 327 | (? ?. (+ digit)) ; Fraction. | |
| 337 | N.B.: Only numbers which can fit in Emacs Lisp's native number | 328 | (? (in "Ee") (? (in ?+ ?-)) (+ digit)))) ; Exponent. |
| 338 | representation will be parsed correctly." | 329 | |
| 339 | ;; If SIGN is non-nil, the number is explicitly signed. | 330 | (defun json-read-number (&optional _sign) |
| 340 | (let ((number-regexp | 331 | "Read the JSON number following point." |
| 341 | "\\([0-9]+\\)?\\(\\.[0-9]+\\)?\\([Ee][+-]?[0-9]+\\)?")) | 332 | (declare (advertised-calling-convention () "28.1")) |
| 342 | (cond ((and (null sign) (= (json-peek) ?-)) | 333 | (or (looking-at (rx json--number)) |
| 343 | (json-advance) | 334 | (signal 'json-number-format (list (point)))) |
| 344 | (- (json-read-number t))) | 335 | (goto-char (match-end 0)) |
| 345 | ((and (null sign) (= (json-peek) ?+)) | 336 | (prog1 (string-to-number (match-string 0)) |
| 346 | (json-advance) | 337 | (or (looking-at-p (rx json--post-value)) |
| 347 | (json-read-number t)) | 338 | (signal 'json-number-format (list (point)))) |
| 348 | ((and (looking-at number-regexp) | 339 | (json-skip-whitespace))) |
| 349 | (or (match-beginning 1) | ||
| 350 | (match-beginning 2))) | ||
| 351 | (goto-char (match-end 0)) | ||
| 352 | (string-to-number (match-string 0))) | ||
| 353 | (t (signal 'json-number-format (list (point))))))) | ||
| 354 | 340 | ||
| 355 | ;; Number encoding | 341 | ;; Number encoding |
| 356 | 342 | ||
| 357 | (defun json-encode-number (number) | 343 | (defalias 'json-encode-number #'number-to-string |
| 358 | "Return a JSON representation of NUMBER." | 344 | "Return a JSON representation of NUMBER.") |
| 359 | (format "%s" number)) | ||
| 360 | 345 | ||
| 361 | ;;; Strings | 346 | ;;; Strings |
| 362 | 347 | ||
| 363 | (defvar json-special-chars | 348 | (defconst json-special-chars |
| 364 | '((?\" . ?\") | 349 | '((?\" . ?\") |
| 365 | (?\\ . ?\\) | 350 | (?\\ . ?\\) |
| 366 | (?b . ?\b) | 351 | (?b . ?\b) |
| @@ -368,7 +353,7 @@ representation will be parsed correctly." | |||
| 368 | (?n . ?\n) | 353 | (?n . ?\n) |
| 369 | (?r . ?\r) | 354 | (?r . ?\r) |
| 370 | (?t . ?\t)) | 355 | (?t . ?\t)) |
| 371 | "Characters which are escaped in JSON, with their elisp counterparts.") | 356 | "Characters which are escaped in JSON, with their Elisp counterparts.") |
| 372 | 357 | ||
| 373 | ;; String parsing | 358 | ;; String parsing |
| 374 | 359 | ||
| @@ -378,48 +363,47 @@ representation will be parsed correctly." | |||
| 378 | 363 | ||
| 379 | (defun json-read-escaped-char () | 364 | (defun json-read-escaped-char () |
| 380 | "Read the JSON string escaped character at point." | 365 | "Read the JSON string escaped character at point." |
| 381 | ;; Skip over the '\' | 366 | ;; Skip over the '\'. |
| 382 | (json-advance) | 367 | (json-advance) |
| 383 | (let* ((char (json-pop)) | 368 | (let ((char (json-pop))) |
| 384 | (special (assq char json-special-chars))) | ||
| 385 | (cond | 369 | (cond |
| 386 | (special (cdr special)) | 370 | ((cdr (assq char json-special-chars))) |
| 387 | ((not (eq char ?u)) char) | 371 | ((/= char ?u) char) |
| 388 | ;; Special-case UTF-16 surrogate pairs, | 372 | ;; Special-case UTF-16 surrogate pairs, |
| 389 | ;; cf. <https://tools.ietf.org/html/rfc7159#section-7>. Note that | 373 | ;; cf. <https://tools.ietf.org/html/rfc7159#section-7>. Note that |
| 390 | ;; this clause overlaps with the next one and therefore has to | 374 | ;; this clause overlaps with the next one and therefore has to |
| 391 | ;; come first. | 375 | ;; come first. |
| 392 | ((looking-at | 376 | ((looking-at |
| 393 | (rx (group (any "Dd") (any "89ABab") (= 2 (any xdigit))) | 377 | (rx (group (any "Dd") (any "89ABab") (= 2 xdigit)) |
| 394 | "\\u" (group (any "Dd") (any "C-Fc-f") (= 2 (any xdigit))))) | 378 | "\\u" (group (any "Dd") (any "C-Fc-f") (= 2 xdigit)))) |
| 395 | (json-advance 10) | 379 | (json-advance 10) |
| 396 | (json--decode-utf-16-surrogates | 380 | (json--decode-utf-16-surrogates |
| 397 | (string-to-number (match-string 1) 16) | 381 | (string-to-number (match-string 1) 16) |
| 398 | (string-to-number (match-string 2) 16))) | 382 | (string-to-number (match-string 2) 16))) |
| 399 | ((looking-at (rx (= 4 xdigit))) | 383 | ((looking-at (rx (= 4 xdigit))) |
| 400 | (let ((hex (match-string 0))) | 384 | (json-advance 4) |
| 401 | (json-advance 4) | 385 | (string-to-number (match-string 0) 16)) |
| 402 | (string-to-number hex 16))) | ||
| 403 | (t | 386 | (t |
| 404 | (signal 'json-string-escape (list (point))))))) | 387 | (signal 'json-string-escape (list (point))))))) |
| 405 | 388 | ||
| 406 | (defun json-read-string () | 389 | (defun json-read-string () |
| 407 | "Read the JSON string at point." | 390 | "Read the JSON string at point." |
| 408 | (unless (= (json-peek) ?\") | 391 | ;; Skip over the '"'. |
| 409 | (signal 'json-string-format (list "doesn't start with `\"'!"))) | ||
| 410 | ;; Skip over the '"' | ||
| 411 | (json-advance) | 392 | (json-advance) |
| 412 | (let ((characters '()) | 393 | (let ((characters '()) |
| 413 | (char (json-peek))) | 394 | (char (json-peek))) |
| 414 | (while (not (= char ?\")) | 395 | (while (/= char ?\") |
| 415 | (when (< char 32) | 396 | (when (< char 32) |
| 416 | (signal 'json-string-format (list (prin1-char char)))) | 397 | (if (zerop char) |
| 398 | (signal 'json-end-of-file ()) | ||
| 399 | (signal 'json-string-format (list char)))) | ||
| 417 | (push (if (= char ?\\) | 400 | (push (if (= char ?\\) |
| 418 | (json-read-escaped-char) | 401 | (json-read-escaped-char) |
| 419 | (json-pop)) | 402 | (json-advance) |
| 403 | char) | ||
| 420 | characters) | 404 | characters) |
| 421 | (setq char (json-peek))) | 405 | (setq char (json-peek))) |
| 422 | ;; Skip over the '"' | 406 | ;; Skip over the '"'. |
| 423 | (json-advance) | 407 | (json-advance) |
| 424 | (if characters | 408 | (if characters |
| 425 | (concat (nreverse characters)) | 409 | (concat (nreverse characters)) |
| @@ -427,29 +411,47 @@ representation will be parsed correctly." | |||
| 427 | 411 | ||
| 428 | ;; String encoding | 412 | ;; String encoding |
| 429 | 413 | ||
| 414 | ;; Escape only quotation mark, backslash, and the control | ||
| 415 | ;; characters U+0000 to U+001F (RFC 4627, ECMA-404). | ||
| 416 | (rx-define json--escape (in ?\" ?\\ cntrl)) | ||
| 417 | |||
| 418 | (defvar json--long-string-threshold 200 | ||
| 419 | "Length above which strings are considered long for JSON encoding. | ||
| 420 | It is generally faster to manipulate such strings in a buffer | ||
| 421 | rather than directly.") | ||
| 422 | |||
| 423 | (defvar json--string-buffer nil | ||
| 424 | "Buffer used for encoding Lisp strings as JSON. | ||
| 425 | Initialized lazily by `json-encode-string'.") | ||
| 426 | |||
| 430 | (defun json-encode-string (string) | 427 | (defun json-encode-string (string) |
| 431 | "Return a JSON representation of STRING." | 428 | "Return a JSON representation of STRING." |
| 432 | ;; Reimplement the meat of `replace-regexp-in-string', for | 429 | ;; Try to avoid buffer overhead in trivial cases, while also |
| 433 | ;; performance (bug#20154). | 430 | ;; avoiding searching pathological strings for escape characters. |
| 434 | (let ((l (length string)) | 431 | ;; Since `string-match-p' doesn't take a LIMIT argument, we use |
| 435 | (start 0) | 432 | ;; string length as our heuristic. See also bug#20154. |
| 436 | res mb) | 433 | (if (and (< (length string) json--long-string-threshold) |
| 437 | ;; Only escape quotation mark, backslash and the control | 434 | (not (string-match-p (rx json--escape) string))) |
| 438 | ;; characters U+0000 to U+001F (RFC 4627, ECMA-404). | 435 | (concat "\"" string "\"") |
| 439 | (while (setq mb (string-match "[\"\\[:cntrl:]]" string start)) | 436 | (with-current-buffer |
| 440 | (let* ((c (aref string mb)) | 437 | (or json--string-buffer |
| 441 | (special (rassq c json-special-chars))) | 438 | (with-current-buffer (generate-new-buffer " *json-string*") |
| 442 | (push (substring string start mb) res) | 439 | ;; This seems to afford decent performance gains. |
| 443 | (push (if special | 440 | (setq-local inhibit-modification-hooks t) |
| 444 | ;; Special JSON character (\n, \r, etc.). | 441 | (setq json--string-buffer (current-buffer)))) |
| 445 | (string ?\\ (car special)) | 442 | (insert ?\" string) |
| 446 | ;; Fallback: UCS code point in \uNNNN form. | 443 | (goto-char (1+ (point-min))) |
| 447 | (format "\\u%04x" c)) | 444 | (while (re-search-forward (rx json--escape) nil 'move) |
| 448 | res) | 445 | (let ((char (preceding-char))) |
| 449 | (setq start (1+ mb)))) | 446 | (delete-char -1) |
| 450 | (push (substring string start l) res) | 447 | (insert ?\\ (or |
| 451 | (push "\"" res) | 448 | ;; Special JSON character (\n, \r, etc.). |
| 452 | (apply #'concat "\"" (nreverse res)))) | 449 | (car (rassq char json-special-chars)) |
| 450 | ;; Fallback: UCS code point in \uNNNN form. | ||
| 451 | (format "u%04x" char))))) | ||
| 452 | (insert ?\") | ||
| 453 | ;; Empty buffer for next invocation. | ||
| 454 | (delete-and-extract-region (point-min) (point-max))))) | ||
| 453 | 455 | ||
| 454 | (defun json-encode-key (object) | 456 | (defun json-encode-key (object) |
| 455 | "Return a JSON representation of OBJECT. | 457 | "Return a JSON representation of OBJECT. |
| @@ -460,15 +462,13 @@ this signals `json-key-format'." | |||
| 460 | (signal 'json-key-format (list object))) | 462 | (signal 'json-key-format (list object))) |
| 461 | encoded)) | 463 | encoded)) |
| 462 | 464 | ||
| 463 | ;;; JSON Objects | 465 | ;;; Objects |
| 464 | 466 | ||
| 465 | (defun json-new-object () | 467 | (defun json-new-object () |
| 466 | "Create a new Elisp object corresponding to a JSON object. | 468 | "Create a new Elisp object corresponding to an empty JSON object. |
| 467 | Please see the documentation of `json-object-type'." | 469 | Please see the documentation of `json-object-type'." |
| 468 | (cond ((eq json-object-type 'hash-table) | 470 | (and (eq json-object-type 'hash-table) |
| 469 | (make-hash-table :test 'equal)) | 471 | (make-hash-table :test #'equal))) |
| 470 | (t | ||
| 471 | ()))) | ||
| 472 | 472 | ||
| 473 | (defun json-add-to-object (object key value) | 473 | (defun json-add-to-object (object key value) |
| 474 | "Add a new KEY -> VALUE association to OBJECT. | 474 | "Add a new KEY -> VALUE association to OBJECT. |
| @@ -476,10 +476,10 @@ Returns the updated object, which you should save, e.g.: | |||
| 476 | (setq obj (json-add-to-object obj \"foo\" \"bar\")) | 476 | (setq obj (json-add-to-object obj \"foo\" \"bar\")) |
| 477 | Please see the documentation of `json-object-type' and `json-key-type'." | 477 | Please see the documentation of `json-object-type' and `json-key-type'." |
| 478 | (let ((json-key-type | 478 | (let ((json-key-type |
| 479 | (or json-key-type | 479 | (cond (json-key-type) |
| 480 | (cdr (assq json-object-type '((hash-table . string) | 480 | ((eq json-object-type 'hash-table) 'string) |
| 481 | (alist . symbol) | 481 | ((eq json-object-type 'alist) 'symbol) |
| 482 | (plist . keyword))))))) | 482 | ((eq json-object-type 'plist) 'keyword)))) |
| 483 | (setq key | 483 | (setq key |
| 484 | (cond ((eq json-key-type 'string) | 484 | (cond ((eq json-key-type 'string) |
| 485 | key) | 485 | key) |
| @@ -499,13 +499,13 @@ Please see the documentation of `json-object-type' and `json-key-type'." | |||
| 499 | 499 | ||
| 500 | (defun json-read-object () | 500 | (defun json-read-object () |
| 501 | "Read the JSON object at point." | 501 | "Read the JSON object at point." |
| 502 | ;; Skip over the "{" | 502 | ;; Skip over the '{'. |
| 503 | (json-advance) | 503 | (json-advance) |
| 504 | (json-skip-whitespace) | 504 | (json-skip-whitespace) |
| 505 | ;; read key/value pairs until "}" | 505 | ;; Read key/value pairs until '}'. |
| 506 | (let ((elements (json-new-object)) | 506 | (let ((elements (json-new-object)) |
| 507 | key value) | 507 | key value) |
| 508 | (while (not (= (json-peek) ?})) | 508 | (while (/= (json-peek) ?\}) |
| 509 | (json-skip-whitespace) | 509 | (json-skip-whitespace) |
| 510 | (setq key (json-read-string)) | 510 | (setq key (json-read-string)) |
| 511 | (json-skip-whitespace) | 511 | (json-skip-whitespace) |
| @@ -520,94 +520,94 @@ Please see the documentation of `json-object-type' and `json-key-type'." | |||
| 520 | (funcall json-post-element-read-function)) | 520 | (funcall json-post-element-read-function)) |
| 521 | (setq elements (json-add-to-object elements key value)) | 521 | (setq elements (json-add-to-object elements key value)) |
| 522 | (json-skip-whitespace) | 522 | (json-skip-whitespace) |
| 523 | (when (/= (json-peek) ?}) | 523 | (when (/= (json-peek) ?\}) |
| 524 | (if (= (json-peek) ?,) | 524 | (if (= (json-peek) ?,) |
| 525 | (json-advance) | 525 | (json-advance) |
| 526 | (signal 'json-object-format (list "," (json-peek)))))) | 526 | (signal 'json-object-format (list "," (json-peek)))))) |
| 527 | ;; Skip over the "}" | 527 | ;; Skip over the '}'. |
| 528 | (json-advance) | 528 | (json-advance) |
| 529 | (pcase json-object-type | 529 | (pcase json-object-type |
| 530 | ('alist (nreverse elements)) | 530 | ('alist (nreverse elements)) |
| 531 | ('plist (json--plist-reverse elements)) | 531 | ('plist (json--plist-nreverse elements)) |
| 532 | (_ elements)))) | 532 | (_ elements)))) |
| 533 | 533 | ||
| 534 | ;; Hash table encoding | 534 | ;; Hash table encoding |
| 535 | 535 | ||
| 536 | (defun json-encode-hash-table (hash-table) | 536 | (defun json-encode-hash-table (hash-table) |
| 537 | "Return a JSON representation of HASH-TABLE." | 537 | "Return a JSON representation of HASH-TABLE." |
| 538 | (if json-encoding-object-sort-predicate | 538 | (cond ((hash-table-empty-p hash-table) "{}") |
| 539 | (json-encode-alist (map-into hash-table 'list)) | 539 | (json-encoding-object-sort-predicate |
| 540 | (format "{%s%s}" | 540 | (json--encode-alist (map-pairs hash-table) t)) |
| 541 | (json-join | 541 | (t |
| 542 | (let (r) | 542 | (let ((kv-sep (if json-encoding-pretty-print ": " ":")) |
| 543 | (json--with-indentation | 543 | result) |
| 544 | (maphash | 544 | (json--with-indentation |
| 545 | (lambda (k v) | 545 | (maphash |
| 546 | (push (format | 546 | (lambda (k v) |
| 547 | (if json-encoding-pretty-print | 547 | (push (concat json--encoding-current-indentation |
| 548 | "%s%s: %s" | 548 | (json-encode-key k) |
| 549 | "%s%s:%s") | 549 | kv-sep |
| 550 | json--encoding-current-indentation | 550 | (json-encode v)) |
| 551 | (json-encode-key k) | 551 | result)) |
| 552 | (json-encode v)) | 552 | hash-table)) |
| 553 | r)) | 553 | (concat "{" |
| 554 | hash-table)) | 554 | (string-join (nreverse result) json-encoding-separator) |
| 555 | r) | 555 | (and json-encoding-pretty-print |
| 556 | json-encoding-separator) | 556 | (not json-encoding-lisp-style-closings) |
| 557 | (if (or (not json-encoding-pretty-print) | 557 | json--encoding-current-indentation) |
| 558 | json-encoding-lisp-style-closings) | 558 | "}"))))) |
| 559 | "" | ||
| 560 | json--encoding-current-indentation)))) | ||
| 561 | 559 | ||
| 562 | ;; List encoding (including alists and plists) | 560 | ;; List encoding (including alists and plists) |
| 563 | 561 | ||
| 564 | (defun json-encode-alist (alist) | 562 | (defun json--encode-alist (alist &optional destructive) |
| 565 | "Return a JSON representation of ALIST." | 563 | "Return a JSON representation of ALIST. |
| 564 | DESTRUCTIVE non-nil means it is safe to modify ALIST by | ||
| 565 | side-effects." | ||
| 566 | (when json-encoding-object-sort-predicate | 566 | (when json-encoding-object-sort-predicate |
| 567 | (setq alist | 567 | (setq alist (sort (if destructive alist (copy-sequence alist)) |
| 568 | (sort alist (lambda (a b) | 568 | (lambda (a b) |
| 569 | (funcall json-encoding-object-sort-predicate | 569 | (funcall json-encoding-object-sort-predicate |
| 570 | (car a) (car b)))))) | 570 | (car a) (car b)))))) |
| 571 | (format "{%s%s}" | 571 | (concat "{" |
| 572 | (json-join | 572 | (let ((kv-sep (if json-encoding-pretty-print ": " ":"))) |
| 573 | (json--with-indentation | 573 | (json--with-indentation |
| 574 | (mapcar (lambda (cons) | 574 | (mapconcat (lambda (cons) |
| 575 | (format (if json-encoding-pretty-print | 575 | (concat json--encoding-current-indentation |
| 576 | "%s%s: %s" | 576 | (json-encode-key (car cons)) |
| 577 | "%s%s:%s") | 577 | kv-sep |
| 578 | json--encoding-current-indentation | 578 | (json-encode (cdr cons)))) |
| 579 | (json-encode-key (car cons)) | 579 | alist |
| 580 | (json-encode (cdr cons)))) | 580 | json-encoding-separator))) |
| 581 | alist)) | 581 | (and json-encoding-pretty-print |
| 582 | json-encoding-separator) | 582 | (not json-encoding-lisp-style-closings) |
| 583 | (if (or (not json-encoding-pretty-print) | 583 | json--encoding-current-indentation) |
| 584 | json-encoding-lisp-style-closings) | 584 | "}")) |
| 585 | "" | 585 | |
| 586 | json--encoding-current-indentation))) | 586 | (defun json-encode-alist (alist) |
| 587 | "Return a JSON representation of ALIST." | ||
| 588 | (if alist (json--encode-alist alist) "{}")) | ||
| 587 | 589 | ||
| 588 | (defun json-encode-plist (plist) | 590 | (defun json-encode-plist (plist) |
| 589 | "Return a JSON representation of PLIST." | 591 | "Return a JSON representation of PLIST." |
| 590 | (if json-encoding-object-sort-predicate | 592 | (cond ((null plist) "{}") |
| 591 | (json-encode-alist (json--plist-to-alist plist)) | 593 | (json-encoding-object-sort-predicate |
| 592 | (let (result) | 594 | (json--encode-alist (map-pairs plist) t)) |
| 593 | (json--with-indentation | 595 | (t |
| 594 | (while plist | 596 | (let ((kv-sep (if json-encoding-pretty-print ": " ":")) |
| 595 | (push (concat | ||
| 596 | json--encoding-current-indentation | ||
| 597 | (json-encode-key (car plist)) | ||
| 598 | (if json-encoding-pretty-print | ||
| 599 | ": " | ||
| 600 | ":") | ||
| 601 | (json-encode (cadr plist))) | ||
| 602 | result) | 597 | result) |
| 603 | (setq plist (cddr plist)))) | 598 | (json--with-indentation |
| 604 | (concat "{" | 599 | (while plist |
| 605 | (json-join (nreverse result) json-encoding-separator) | 600 | (push (concat json--encoding-current-indentation |
| 606 | (if (and json-encoding-pretty-print | 601 | (json-encode-key (pop plist)) |
| 607 | (not json-encoding-lisp-style-closings)) | 602 | kv-sep |
| 608 | json--encoding-current-indentation | 603 | (json-encode (pop plist))) |
| 609 | "") | 604 | result))) |
| 610 | "}")))) | 605 | (concat "{" |
| 606 | (string-join (nreverse result) json-encoding-separator) | ||
| 607 | (and json-encoding-pretty-print | ||
| 608 | (not json-encoding-lisp-style-closings) | ||
| 609 | json--encoding-current-indentation) | ||
| 610 | "}"))))) | ||
| 611 | 611 | ||
| 612 | (defun json-encode-list (list) | 612 | (defun json-encode-list (list) |
| 613 | "Return a JSON representation of LIST. | 613 | "Return a JSON representation of LIST. |
| @@ -625,15 +625,17 @@ become JSON objects." | |||
| 625 | 625 | ||
| 626 | (defun json-read-array () | 626 | (defun json-read-array () |
| 627 | "Read the JSON array at point." | 627 | "Read the JSON array at point." |
| 628 | ;; Skip over the "[" | 628 | ;; Skip over the '['. |
| 629 | (json-advance) | 629 | (json-advance) |
| 630 | (json-skip-whitespace) | 630 | (json-skip-whitespace) |
| 631 | ;; read values until "]" | 631 | ;; Read values until ']'. |
| 632 | (let (elements) | 632 | (let (elements |
| 633 | (while (not (= (json-peek) ?\])) | 633 | (len 0)) |
| 634 | (while (/= (json-peek) ?\]) | ||
| 634 | (json-skip-whitespace) | 635 | (json-skip-whitespace) |
| 635 | (when json-pre-element-read-function | 636 | (when json-pre-element-read-function |
| 636 | (funcall json-pre-element-read-function (length elements))) | 637 | (funcall json-pre-element-read-function len) |
| 638 | (setq len (1+ len))) | ||
| 637 | (push (json-read) elements) | 639 | (push (json-read) elements) |
| 638 | (when json-post-element-read-function | 640 | (when json-post-element-read-function |
| 639 | (funcall json-post-element-read-function)) | 641 | (funcall json-post-element-read-function)) |
| @@ -641,8 +643,8 @@ become JSON objects." | |||
| 641 | (when (/= (json-peek) ?\]) | 643 | (when (/= (json-peek) ?\]) |
| 642 | (if (= (json-peek) ?,) | 644 | (if (= (json-peek) ?,) |
| 643 | (json-advance) | 645 | (json-advance) |
| 644 | (signal 'json-array-format (list ?, (json-peek)))))) | 646 | (signal 'json-array-format (list "," (json-peek)))))) |
| 645 | ;; Skip over the "]" | 647 | ;; Skip over the ']'. |
| 646 | (json-advance) | 648 | (json-advance) |
| 647 | (pcase json-array-type | 649 | (pcase json-array-type |
| 648 | ('vector (nreverse (vconcat elements))) | 650 | ('vector (nreverse (vconcat elements))) |
| @@ -653,42 +655,43 @@ become JSON objects." | |||
| 653 | (defun json-encode-array (array) | 655 | (defun json-encode-array (array) |
| 654 | "Return a JSON representation of ARRAY." | 656 | "Return a JSON representation of ARRAY." |
| 655 | (if (and json-encoding-pretty-print | 657 | (if (and json-encoding-pretty-print |
| 656 | (> (length array) 0)) | 658 | (not (seq-empty-p array))) |
| 657 | (concat | 659 | (concat |
| 660 | "[" | ||
| 658 | (json--with-indentation | 661 | (json--with-indentation |
| 659 | (concat (format "[%s" json--encoding-current-indentation) | 662 | (concat json--encoding-current-indentation |
| 660 | (json-join (mapcar 'json-encode array) | 663 | (mapconcat #'json-encode array |
| 661 | (format "%s%s" | 664 | (concat json-encoding-separator |
| 662 | json-encoding-separator | ||
| 663 | json--encoding-current-indentation)))) | 665 | json--encoding-current-indentation)))) |
| 664 | (format "%s]" | 666 | (unless json-encoding-lisp-style-closings |
| 665 | (if json-encoding-lisp-style-closings | 667 | json--encoding-current-indentation) |
| 666 | "" | 668 | "]") |
| 667 | json--encoding-current-indentation))) | ||
| 668 | (concat "[" | 669 | (concat "[" |
| 669 | (mapconcat 'json-encode array json-encoding-separator) | 670 | (mapconcat #'json-encode array json-encoding-separator) |
| 670 | "]"))) | 671 | "]"))) |
| 671 | 672 | ||
| 672 | 673 | ||
| 673 | 674 | ||
| 674 | ;;; JSON reader. | 675 | ;;; Reader |
| 675 | 676 | ||
| 676 | (defmacro json-readtable-dispatch (char) | 677 | (defmacro json-readtable-dispatch (char) |
| 677 | "Dispatch reader function for CHAR." | 678 | "Dispatch reader function for CHAR at point. |
| 678 | (declare (debug (symbolp))) | 679 | If CHAR is nil, signal `json-end-of-file'." |
| 679 | (let ((table | 680 | (declare (debug t)) |
| 680 | '((?t json-read-keyword "true") | 681 | (macroexp-let2 nil char char |
| 681 | (?f json-read-keyword "false") | 682 | `(cond ,@(map-apply |
| 682 | (?n json-read-keyword "null") | 683 | (lambda (key expr) |
| 683 | (?{ json-read-object) | 684 | `((eq ,char ,key) ,expr)) |
| 684 | (?\[ json-read-array) | 685 | `((?\" ,#'json-read-string) |
| 685 | (?\" json-read-string))) | 686 | (?\[ ,#'json-read-array) |
| 686 | res) | 687 | (?\{ ,#'json-read-object) |
| 687 | (dolist (c '(?- ?+ ?. ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9)) | 688 | (?n ,#'json-read-keyword "null") |
| 688 | (push (list c 'json-read-number) table)) | 689 | (?f ,#'json-read-keyword "false") |
| 689 | (pcase-dolist (`(,c . ,rest) table) | 690 | (?t ,#'json-read-keyword "true") |
| 690 | (push `((eq ,char ,c) (,@rest)) res)) | 691 | ,@(mapcar (lambda (c) (list c #'json-read-number)) |
| 691 | `(cond ,@res (t (signal 'json-readtable-error (list ,char)))))) | 692 | '(?- ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9)))) |
| 693 | (,char (signal 'json-readtable-error (list ,char))) | ||
| 694 | (t (signal 'json-end-of-file ()))))) | ||
| 692 | 695 | ||
| 693 | (defun json-read () | 696 | (defun json-read () |
| 694 | "Parse and return the JSON object following point. | 697 | "Parse and return the JSON object following point. |
| @@ -706,10 +709,7 @@ you will get the following structure returned: | |||
| 706 | ((c . :json-false))]) | 709 | ((c . :json-false))]) |
| 707 | (b . \"foo\"))" | 710 | (b . \"foo\"))" |
| 708 | (json-skip-whitespace) | 711 | (json-skip-whitespace) |
| 709 | (let ((char (json-peek))) | 712 | (json-readtable-dispatch (char-after))) |
| 710 | (if (zerop char) | ||
| 711 | (signal 'json-end-of-file nil) | ||
| 712 | (json-readtable-dispatch char)))) | ||
| 713 | 713 | ||
| 714 | ;; Syntactic sugar for the reader | 714 | ;; Syntactic sugar for the reader |
| 715 | 715 | ||
| @@ -724,12 +724,11 @@ you will get the following structure returned: | |||
| 724 | "Read the first JSON object contained in FILE and return it." | 724 | "Read the first JSON object contained in FILE and return it." |
| 725 | (with-temp-buffer | 725 | (with-temp-buffer |
| 726 | (insert-file-contents file) | 726 | (insert-file-contents file) |
| 727 | (goto-char (point-min)) | ||
| 728 | (json-read))) | 727 | (json-read))) |
| 729 | 728 | ||
| 730 | 729 | ||
| 731 | 730 | ||
| 732 | ;;; JSON encoder | 731 | ;;; Encoder |
| 733 | 732 | ||
| 734 | (defun json-encode (object) | 733 | (defun json-encode (object) |
| 735 | "Return a JSON representation of OBJECT as a string. | 734 | "Return a JSON representation of OBJECT as a string. |
| @@ -737,20 +736,21 @@ you will get the following structure returned: | |||
| 737 | OBJECT should have a structure like one returned by `json-read'. | 736 | OBJECT should have a structure like one returned by `json-read'. |
| 738 | If an error is detected during encoding, an error based on | 737 | If an error is detected during encoding, an error based on |
| 739 | `json-error' is signaled." | 738 | `json-error' is signaled." |
| 740 | (cond ((memq object (list t json-null json-false)) | 739 | (cond ((eq object t) (json-encode-keyword object)) |
| 741 | (json-encode-keyword object)) | 740 | ((eq object json-null) (json-encode-keyword object)) |
| 742 | ((stringp object) (json-encode-string object)) | 741 | ((eq object json-false) (json-encode-keyword object)) |
| 743 | ((keywordp object) (json-encode-string | 742 | ((stringp object) (json-encode-string object)) |
| 744 | (substring (symbol-name object) 1))) | 743 | ((keywordp object) (json-encode-string |
| 745 | ((listp object) (json-encode-list object)) | 744 | (substring (symbol-name object) 1))) |
| 746 | ((symbolp object) (json-encode-string | 745 | ((listp object) (json-encode-list object)) |
| 747 | (symbol-name object))) | 746 | ((symbolp object) (json-encode-string |
| 748 | ((numberp object) (json-encode-number object)) | 747 | (symbol-name object))) |
| 749 | ((arrayp object) (json-encode-array object)) | 748 | ((numberp object) (json-encode-number object)) |
| 750 | ((hash-table-p object) (json-encode-hash-table object)) | 749 | ((arrayp object) (json-encode-array object)) |
| 751 | (t (signal 'json-error (list object))))) | 750 | ((hash-table-p object) (json-encode-hash-table object)) |
| 752 | 751 | (t (signal 'json-error (list object))))) | |
| 753 | ;; Pretty printing & minimizing | 752 | |
| 753 | ;;; Pretty printing & minimizing | ||
| 754 | 754 | ||
| 755 | (defun json-pretty-print-buffer (&optional minimize) | 755 | (defun json-pretty-print-buffer (&optional minimize) |
| 756 | "Pretty-print current buffer. | 756 | "Pretty-print current buffer. |
| @@ -769,9 +769,9 @@ MAX-SECS.") | |||
| 769 | With prefix argument MINIMIZE, minimize it instead." | 769 | With prefix argument MINIMIZE, minimize it instead." |
| 770 | (interactive "r\nP") | 770 | (interactive "r\nP") |
| 771 | (let ((json-encoding-pretty-print (null minimize)) | 771 | (let ((json-encoding-pretty-print (null minimize)) |
| 772 | ;; Distinguish an empty objects from 'null' | 772 | ;; Distinguish an empty object from 'null'. |
| 773 | (json-null :json-null) | 773 | (json-null :json-null) |
| 774 | ;; Ensure that ordering is maintained | 774 | ;; Ensure that ordering is maintained. |
| 775 | (json-object-type 'alist) | 775 | (json-object-type 'alist) |
| 776 | (orig-buf (current-buffer)) | 776 | (orig-buf (current-buffer)) |
| 777 | error) | 777 | error) |
| @@ -800,9 +800,7 @@ With prefix argument MINIMIZE, minimize it instead." | |||
| 800 | ;; them. | 800 | ;; them. |
| 801 | (let ((space (buffer-substring | 801 | (let ((space (buffer-substring |
| 802 | (point) | 802 | (point) |
| 803 | (+ (point) | 803 | (+ (point) (skip-chars-forward " \t\n")))) |
| 804 | (skip-chars-forward | ||
| 805 | " \t\n" (point-max))))) | ||
| 806 | (json (json-read))) | 804 | (json (json-read))) |
| 807 | (setq pos (point)) ; End of last good json-read. | 805 | (setq pos (point)) ; End of last good json-read. |
| 808 | (set-buffer tmp-buf) | 806 | (set-buffer tmp-buf) |
| @@ -832,14 +830,14 @@ With prefix argument MINIMIZE, minimize it instead." | |||
| 832 | "Pretty-print current buffer with object keys ordered. | 830 | "Pretty-print current buffer with object keys ordered. |
| 833 | With prefix argument MINIMIZE, minimize it instead." | 831 | With prefix argument MINIMIZE, minimize it instead." |
| 834 | (interactive "P") | 832 | (interactive "P") |
| 835 | (let ((json-encoding-object-sort-predicate 'string<)) | 833 | (let ((json-encoding-object-sort-predicate #'string<)) |
| 836 | (json-pretty-print-buffer minimize))) | 834 | (json-pretty-print-buffer minimize))) |
| 837 | 835 | ||
| 838 | (defun json-pretty-print-ordered (begin end &optional minimize) | 836 | (defun json-pretty-print-ordered (begin end &optional minimize) |
| 839 | "Pretty-print the region with object keys ordered. | 837 | "Pretty-print the region with object keys ordered. |
| 840 | With prefix argument MINIMIZE, minimize it instead." | 838 | With prefix argument MINIMIZE, minimize it instead." |
| 841 | (interactive "r\nP") | 839 | (interactive "r\nP") |
| 842 | (let ((json-encoding-object-sort-predicate 'string<)) | 840 | (let ((json-encoding-object-sort-predicate #'string<)) |
| 843 | (json-pretty-print begin end minimize))) | 841 | (json-pretty-print begin end minimize))) |
| 844 | 842 | ||
| 845 | (provide 'json) | 843 | (provide 'json) |
diff --git a/lisp/jsonrpc.el b/lisp/jsonrpc.el index 293dfaa7483..42e7701af18 100644 --- a/lisp/jsonrpc.el +++ b/lisp/jsonrpc.el | |||
| @@ -37,7 +37,6 @@ | |||
| 37 | ;;; Code: | 37 | ;;; Code: |
| 38 | 38 | ||
| 39 | (require 'cl-lib) | 39 | (require 'cl-lib) |
| 40 | (require 'json) | ||
| 41 | (require 'eieio) | 40 | (require 'eieio) |
| 42 | (eval-when-compile (require 'subr-x)) | 41 | (eval-when-compile (require 'subr-x)) |
| 43 | (require 'warnings) | 42 | (require 'warnings) |
| @@ -470,26 +469,35 @@ With optional CLEANUP, kill any associated buffers." | |||
| 470 | ;;; | 469 | ;;; |
| 471 | (define-error 'jsonrpc-error "jsonrpc-error") | 470 | (define-error 'jsonrpc-error "jsonrpc-error") |
| 472 | 471 | ||
| 473 | (defun jsonrpc--json-read () | 472 | (defalias 'jsonrpc--json-read |
| 474 | "Read JSON object in buffer, move point to end of buffer." | 473 | (if (fboundp 'json-parse-buffer) |
| 475 | ;; TODO: I guess we can make these macros if/when jsonrpc.el | 474 | (lambda () |
| 476 | ;; goes into Emacs core. | 475 | (json-parse-buffer :object-type 'plist |
| 477 | (cond ((fboundp 'json-parse-buffer) (json-parse-buffer | 476 | :null-object nil |
| 478 | :object-type 'plist | 477 | :false-object :json-false)) |
| 479 | :null-object nil | 478 | (require 'json) |
| 480 | :false-object :json-false)) | 479 | (defvar json-object-type) |
| 481 | (t (let ((json-object-type 'plist)) | 480 | (declare-function json-read "json" ()) |
| 482 | (json-read))))) | 481 | (lambda () |
| 483 | 482 | (let ((json-object-type 'plist)) | |
| 484 | (defun jsonrpc--json-encode (object) | 483 | (json-read)))) |
| 485 | "Encode OBJECT into a JSON string." | 484 | "Read JSON object in buffer, move point to end of buffer.") |
| 486 | (cond ((fboundp 'json-serialize) (json-serialize | 485 | |
| 487 | object | 486 | (defalias 'jsonrpc--json-encode |
| 488 | :false-object :json-false | 487 | (if (fboundp 'json-serialize) |
| 489 | :null-object nil)) | 488 | (lambda (object) |
| 490 | (t (let ((json-false :json-false) | 489 | (json-serialize object |
| 491 | (json-null nil)) | 490 | :false-object :json-false |
| 492 | (json-encode object))))) | 491 | :null-object nil)) |
| 492 | (require 'json) | ||
| 493 | (defvar json-false) | ||
| 494 | (defvar json-null) | ||
| 495 | (declare-function json-encode "json" (object)) | ||
| 496 | (lambda (object) | ||
| 497 | (let ((json-false :json-false) | ||
| 498 | (json-null nil)) | ||
| 499 | (json-encode object)))) | ||
| 500 | "Encode OBJECT into a JSON string.") | ||
| 493 | 501 | ||
| 494 | (cl-defun jsonrpc--reply | 502 | (cl-defun jsonrpc--reply |
| 495 | (connection id &key (result nil result-supplied-p) (error nil error-supplied-p)) | 503 | (connection id &key (result nil result-supplied-p) (error nil error-supplied-p)) |
diff --git a/lisp/language/tibet-util.el b/lisp/language/tibet-util.el index 29fff9175b7..8684cdb1338 100644 --- a/lisp/language/tibet-util.el +++ b/lisp/language/tibet-util.el | |||
| @@ -43,13 +43,17 @@ | |||
| 43 | ("་" . "་") | 43 | ("་" . "་") |
| 44 | ("༔" . "༔") | 44 | ("༔" . "༔") |
| 45 | ;; Yes these are dirty. But ... | 45 | ;; Yes these are dirty. But ... |
| 46 | ("༎ ༎" . ,(compose-string "༎ ༎" 0 3 [?༎ (Br . Bl) ? (Br . Bl) ?༎])) | 46 | ("༎ ༎" . ,(compose-string (copy-sequence "༎ ༎") |
| 47 | 0 3 [?༎ (Br . Bl) ? (Br . Bl) ?༎])) | ||
| 47 | ("༄༅༅" . ,(compose-string | 48 | ("༄༅༅" . ,(compose-string |
| 48 | "࿁࿂࿂࿂" 0 4 | 49 | (copy-sequence "࿁࿂࿂࿂") 0 4 |
| 49 | [?࿁ (Br . Bl) ?࿂ (Br . Bl) ?࿂ (Br . Bl) ?࿂])) | 50 | [?࿁ (Br . Bl) ?࿂ (Br . Bl) ?࿂ (Br . Bl) ?࿂])) |
| 50 | ("༄༅" . ,(compose-string "࿁࿂࿂" 0 3 [?࿁ (Br . Bl) ?࿂ (Br . Bl) ?࿂])) | 51 | ("༄༅" . ,(compose-string (copy-sequence "࿁࿂࿂") |
| 51 | ("༆" . ,(compose-string "࿁࿂༙" 0 3 [?࿁ (Br . Bl) ?࿂ (br . tr) ?༙])) | 52 | 0 3 [?࿁ (Br . Bl) ?࿂ (Br . Bl) ?࿂])) |
| 52 | ("༄" . ,(compose-string "࿁࿂" 0 2 [?࿁ (Br . Bl) ?࿂])))) | 53 | ("༆" . ,(compose-string (copy-sequence "࿁࿂༙") |
| 54 | 0 3 [?࿁ (Br . Bl) ?࿂ (br . tr) ?༙])) | ||
| 55 | ("༄" . ,(compose-string (copy-sequence "࿁࿂") | ||
| 56 | 0 2 [?࿁ (Br . Bl) ?࿂])))) | ||
| 53 | 57 | ||
| 54 | ;;;###autoload | 58 | ;;;###autoload |
| 55 | (defun tibetan-char-p (ch) | 59 | (defun tibetan-char-p (ch) |
diff --git a/lisp/mouse.el b/lisp/mouse.el index f045e5bdce2..640f10af4e1 100644 --- a/lisp/mouse.el +++ b/lisp/mouse.el | |||
| @@ -2580,7 +2580,7 @@ in a tooltip." | |||
| 2580 | :type '(choice | 2580 | :type '(choice |
| 2581 | (const :tag "Do not show tooltips" nil) | 2581 | (const :tag "Do not show tooltips" nil) |
| 2582 | (const :tag "Show all text" t) | 2582 | (const :tag "Show all text" t) |
| 2583 | (integer :tag "Show characters (max)" 256)) | 2583 | (integer :tag "Max number of characters to show" 256)) |
| 2584 | :version "26.1") | 2584 | :version "26.1") |
| 2585 | 2585 | ||
| 2586 | (defcustom mouse-drag-and-drop-region-show-cursor t | 2586 | (defcustom mouse-drag-and-drop-region-show-cursor t |
diff --git a/lisp/net/eww.el b/lisp/net/eww.el index a6c1abdbb19..2a70560ca7b 100644 --- a/lisp/net/eww.el +++ b/lisp/net/eww.el | |||
| @@ -307,10 +307,10 @@ the default EWW buffer." | |||
| 307 | (insert (format "Loading %s..." url)) | 307 | (insert (format "Loading %s..." url)) |
| 308 | (goto-char (point-min))) | 308 | (goto-char (point-min))) |
| 309 | (let ((url-mime-accept-string eww-accept-content-types)) | 309 | (let ((url-mime-accept-string eww-accept-content-types)) |
| 310 | (url-retrieve url 'eww-render | 310 | (url-retrieve url #'eww-render |
| 311 | (list url nil (current-buffer))))) | 311 | (list url nil (current-buffer))))) |
| 312 | 312 | ||
| 313 | (put 'eww 'browse-url-browser-kind 'internal) | 313 | (function-put 'eww 'browse-url-browser-kind 'internal) |
| 314 | 314 | ||
| 315 | (defun eww--dwim-expand-url (url) | 315 | (defun eww--dwim-expand-url (url) |
| 316 | (setq url (string-trim url)) | 316 | (setq url (string-trim url)) |
| @@ -375,8 +375,8 @@ engine used." | |||
| 375 | (let ((region-string (buffer-substring (region-beginning) (region-end)))) | 375 | (let ((region-string (buffer-substring (region-beginning) (region-end)))) |
| 376 | (if (not (string-match-p "\\`[ \n\t\r\v\f]*\\'" region-string)) | 376 | (if (not (string-match-p "\\`[ \n\t\r\v\f]*\\'" region-string)) |
| 377 | (eww region-string) | 377 | (eww region-string) |
| 378 | (call-interactively 'eww))) | 378 | (call-interactively #'eww))) |
| 379 | (call-interactively 'eww))) | 379 | (call-interactively #'eww))) |
| 380 | 380 | ||
| 381 | (defun eww-open-in-new-buffer () | 381 | (defun eww-open-in-new-buffer () |
| 382 | "Fetch link at point in a new EWW buffer." | 382 | "Fetch link at point in a new EWW buffer." |
| @@ -1013,7 +1013,7 @@ just re-display the HTML already fetched." | |||
| 1013 | (eww-display-html 'utf-8 url (plist-get eww-data :dom) | 1013 | (eww-display-html 'utf-8 url (plist-get eww-data :dom) |
| 1014 | (point) (current-buffer))) | 1014 | (point) (current-buffer))) |
| 1015 | (let ((url-mime-accept-string eww-accept-content-types)) | 1015 | (let ((url-mime-accept-string eww-accept-content-types)) |
| 1016 | (url-retrieve url 'eww-render | 1016 | (url-retrieve url #'eww-render |
| 1017 | (list url (point) (current-buffer) encode)))))) | 1017 | (list url (point) (current-buffer) encode)))))) |
| 1018 | 1018 | ||
| 1019 | ;; Form support. | 1019 | ;; Form support. |
| @@ -1576,8 +1576,10 @@ If EXTERNAL is double prefix, browse in new buffer." | |||
| 1576 | (cond | 1576 | (cond |
| 1577 | ((not url) | 1577 | ((not url) |
| 1578 | (message "No link under point")) | 1578 | (message "No link under point")) |
| 1579 | ((string-match "^mailto:" url) | 1579 | ((string-match-p "\\`mailto:" url) |
| 1580 | (browse-url-mail url)) | 1580 | ;; This respects the user options `browse-url-handlers' |
| 1581 | ;; and `browse-url-mailto-function'. | ||
| 1582 | (browse-url url)) | ||
| 1581 | ((and (consp external) (<= (car external) 4)) | 1583 | ((and (consp external) (<= (car external) 4)) |
| 1582 | (funcall browse-url-secondary-browser-function url) | 1584 | (funcall browse-url-secondary-browser-function url) |
| 1583 | (shr--blink-link)) | 1585 | (shr--blink-link)) |
| @@ -1615,7 +1617,7 @@ Use link at point if there is one, else the current page's URL." | |||
| 1615 | (eww-current-url)))) | 1617 | (eww-current-url)))) |
| 1616 | (if (not url) | 1618 | (if (not url) |
| 1617 | (message "No URL under point") | 1619 | (message "No URL under point") |
| 1618 | (url-retrieve url 'eww-download-callback (list url))))) | 1620 | (url-retrieve url #'eww-download-callback (list url))))) |
| 1619 | 1621 | ||
| 1620 | (defun eww-download-callback (status url) | 1622 | (defun eww-download-callback (status url) |
| 1621 | (unless (plist-get status :error) | 1623 | (unless (plist-get status :error) |
| @@ -2128,12 +2130,12 @@ entries (if any) will be removed from the list. | |||
| 2128 | Only the properties listed in `eww-desktop-data-save' are included. | 2130 | Only the properties listed in `eww-desktop-data-save' are included. |
| 2129 | Generally, the list should not include the (usually overly large) | 2131 | Generally, the list should not include the (usually overly large) |
| 2130 | :dom, :source and :text properties." | 2132 | :dom, :source and :text properties." |
| 2131 | (let ((history (mapcar 'eww-desktop-data-1 | 2133 | (let ((history (mapcar #'eww-desktop-data-1 |
| 2132 | (cons eww-data eww-history)))) | 2134 | (cons eww-data eww-history)))) |
| 2133 | (list :history (if eww-desktop-remove-duplicates | 2135 | (list :history (if eww-desktop-remove-duplicates |
| 2134 | (cl-remove-duplicates | 2136 | (cl-remove-duplicates |
| 2135 | history :test 'eww-desktop-history-duplicate) | 2137 | history :test #'eww-desktop-history-duplicate) |
| 2136 | history)))) | 2138 | history)))) |
| 2137 | 2139 | ||
| 2138 | (defun eww-restore-desktop (file-name buffer-name misc-data) | 2140 | (defun eww-restore-desktop (file-name buffer-name misc-data) |
| 2139 | "Restore an eww buffer from its desktop file record. | 2141 | "Restore an eww buffer from its desktop file record. |
diff --git a/lisp/net/shr.el b/lisp/net/shr.el index 1f80ab74db5..03260c9e70a 100644 --- a/lisp/net/shr.el +++ b/lisp/net/shr.el | |||
| @@ -135,7 +135,7 @@ same domain as the main data." | |||
| 135 | This is used for cid: URLs, and the function is called with the | 135 | This is used for cid: URLs, and the function is called with the |
| 136 | cid: URL as the argument.") | 136 | cid: URL as the argument.") |
| 137 | 137 | ||
| 138 | (defvar shr-put-image-function 'shr-put-image | 138 | (defvar shr-put-image-function #'shr-put-image |
| 139 | "Function called to put image and alt string.") | 139 | "Function called to put image and alt string.") |
| 140 | 140 | ||
| 141 | (defface shr-strike-through '((t :strike-through t)) | 141 | (defface shr-strike-through '((t :strike-through t)) |
| @@ -365,25 +365,20 @@ If the URL is already at the front of the kill ring act like | |||
| 365 | (shr-copy-url url))) | 365 | (shr-copy-url url))) |
| 366 | 366 | ||
| 367 | (defun shr--current-link-region () | 367 | (defun shr--current-link-region () |
| 368 | (let ((current (get-text-property (point) 'shr-url)) | 368 | "Return the start and end positions of the URL at point, if any. |
| 369 | start) | 369 | Value is a pair of positions (START . END) if there is a non-nil |
| 370 | (save-excursion | 370 | `shr-url' text property at point; otherwise nil." |
| 371 | ;; Go to the beginning. | 371 | (when (get-text-property (point) 'shr-url) |
| 372 | (while (and (not (bobp)) | 372 | (let* ((end (or (next-single-property-change (point) 'shr-url) |
| 373 | (equal (get-text-property (point) 'shr-url) current)) | 373 | (point-max))) |
| 374 | (forward-char -1)) | 374 | (beg (or (previous-single-property-change end 'shr-url) |
| 375 | (unless (equal (get-text-property (point) 'shr-url) current) | 375 | (point-min)))) |
| 376 | (forward-char 1)) | 376 | (cons beg end)))) |
| 377 | (setq start (point)) | ||
| 378 | ;; Go to the end. | ||
| 379 | (while (and (not (eobp)) | ||
| 380 | (equal (get-text-property (point) 'shr-url) current)) | ||
| 381 | (forward-char 1)) | ||
| 382 | (list start (point))))) | ||
| 383 | 377 | ||
| 384 | (defun shr--blink-link () | 378 | (defun shr--blink-link () |
| 385 | (let* ((region (shr--current-link-region)) | 379 | "Briefly fontify URL at point with the face `shr-selected-link'." |
| 386 | (overlay (make-overlay (car region) (cadr region)))) | 380 | (when-let* ((region (shr--current-link-region)) |
| 381 | (overlay (make-overlay (car region) (cdr region)))) | ||
| 387 | (overlay-put overlay 'face 'shr-selected-link) | 382 | (overlay-put overlay 'face 'shr-selected-link) |
| 388 | (run-at-time 1 nil (lambda () | 383 | (run-at-time 1 nil (lambda () |
| 389 | (delete-overlay overlay))))) | 384 | (delete-overlay overlay))))) |
| @@ -437,7 +432,7 @@ the URL of the image to the kill buffer instead." | |||
| 437 | (if (not url) | 432 | (if (not url) |
| 438 | (message "No image under point") | 433 | (message "No image under point") |
| 439 | (message "Inserting %s..." url) | 434 | (message "Inserting %s..." url) |
| 440 | (url-retrieve url 'shr-image-fetched | 435 | (url-retrieve url #'shr-image-fetched |
| 441 | (list (current-buffer) (1- (point)) (point-marker)) | 436 | (list (current-buffer) (1- (point)) (point-marker)) |
| 442 | t)))) | 437 | t)))) |
| 443 | 438 | ||
| @@ -463,7 +458,7 @@ size, and full-buffer size." | |||
| 463 | (when (> (- (point) start) 2) | 458 | (when (> (- (point) start) 2) |
| 464 | (delete-region start (1- (point))))) | 459 | (delete-region start (1- (point))))) |
| 465 | (message "Inserting %s..." url) | 460 | (message "Inserting %s..." url) |
| 466 | (url-retrieve url 'shr-image-fetched | 461 | (url-retrieve url #'shr-image-fetched |
| 467 | (list (current-buffer) (1- (point)) (point-marker) | 462 | (list (current-buffer) (1- (point)) (point-marker) |
| 468 | (list (cons 'size | 463 | (list (cons 'size |
| 469 | (cond ((or (eq size 'default) | 464 | (cond ((or (eq size 'default) |
| @@ -493,7 +488,7 @@ size, and full-buffer size." | |||
| 493 | ((fboundp function) | 488 | ((fboundp function) |
| 494 | (apply function dom args)) | 489 | (apply function dom args)) |
| 495 | (t | 490 | (t |
| 496 | (apply 'shr-generic dom args))))) | 491 | (apply #'shr-generic dom args))))) |
| 497 | 492 | ||
| 498 | (defun shr-descend (dom) | 493 | (defun shr-descend (dom) |
| 499 | (let ((function | 494 | (let ((function |
| @@ -730,9 +725,10 @@ size, and full-buffer size." | |||
| 730 | (let ((gap-start (point)) | 725 | (let ((gap-start (point)) |
| 731 | (face (get-text-property (point) 'face))) | 726 | (face (get-text-property (point) 'face))) |
| 732 | ;; Extend the background to the end of the line. | 727 | ;; Extend the background to the end of the line. |
| 733 | (if face | 728 | (insert ?\n) |
| 734 | (insert (propertize "\n" 'face (shr-face-background face))) | 729 | (when face |
| 735 | (insert "\n")) | 730 | (put-text-property (1- (point)) (point) |
| 731 | 'face (shr-face-background face))) | ||
| 736 | (shr-indent) | 732 | (shr-indent) |
| 737 | (when (and (> (1- gap-start) (point-min)) | 733 | (when (and (> (1- gap-start) (point-min)) |
| 738 | (get-text-property (point) 'shr-url) | 734 | (get-text-property (point) 'shr-url) |
| @@ -935,12 +931,11 @@ size, and full-buffer size." | |||
| 935 | 931 | ||
| 936 | (defun shr-indent () | 932 | (defun shr-indent () |
| 937 | (when (> shr-indentation 0) | 933 | (when (> shr-indentation 0) |
| 938 | (insert | 934 | (if (not shr-use-fonts) |
| 939 | (if (not shr-use-fonts) | 935 | (insert-char ?\s shr-indentation) |
| 940 | (make-string shr-indentation ?\s) | 936 | (insert ?\s) |
| 941 | (propertize " " | 937 | (put-text-property (1- (point)) (point) |
| 942 | 'display | 938 | 'display `(space :width (,shr-indentation)))))) |
| 943 | `(space :width (,shr-indentation))))))) | ||
| 944 | 939 | ||
| 945 | (defun shr-fontize-dom (dom &rest types) | 940 | (defun shr-fontize-dom (dom &rest types) |
| 946 | (let ((start (point))) | 941 | (let ((start (point))) |
| @@ -987,16 +982,11 @@ the mouse click event." | |||
| 987 | (cond | 982 | (cond |
| 988 | ((not url) | 983 | ((not url) |
| 989 | (message "No link under point")) | 984 | (message "No link under point")) |
| 990 | ((string-match "^mailto:" url) | 985 | (external |
| 991 | (browse-url-mail url)) | 986 | (funcall browse-url-secondary-browser-function url) |
| 987 | (shr--blink-link)) | ||
| 992 | (t | 988 | (t |
| 993 | (if external | 989 | (browse-url url (xor new-window browse-url-new-window-flag)))))) |
| 994 | (progn | ||
| 995 | (funcall browse-url-secondary-browser-function url) | ||
| 996 | (shr--blink-link)) | ||
| 997 | (browse-url url (if new-window | ||
| 998 | (not browse-url-new-window-flag) | ||
| 999 | browse-url-new-window-flag))))))) | ||
| 1000 | 990 | ||
| 1001 | (defun shr-save-contents (directory) | 991 | (defun shr-save-contents (directory) |
| 1002 | "Save the contents from URL in a file." | 992 | "Save the contents from URL in a file." |
| @@ -1005,7 +995,7 @@ the mouse click event." | |||
| 1005 | (if (not url) | 995 | (if (not url) |
| 1006 | (message "No link under point") | 996 | (message "No link under point") |
| 1007 | (url-retrieve (shr-encode-url url) | 997 | (url-retrieve (shr-encode-url url) |
| 1008 | 'shr-store-contents (list url directory))))) | 998 | #'shr-store-contents (list url directory))))) |
| 1009 | 999 | ||
| 1010 | (defun shr-store-contents (status url directory) | 1000 | (defun shr-store-contents (status url directory) |
| 1011 | (unless (plist-get status :error) | 1001 | (unless (plist-get status :error) |
| @@ -1156,7 +1146,6 @@ width/height instead." | |||
| 1156 | 1146 | ||
| 1157 | ;; url-cache-extract autoloads url-cache. | 1147 | ;; url-cache-extract autoloads url-cache. |
| 1158 | (declare-function url-cache-create-filename "url-cache" (url)) | 1148 | (declare-function url-cache-create-filename "url-cache" (url)) |
| 1159 | (autoload 'browse-url-mail "browse-url") | ||
| 1160 | 1149 | ||
| 1161 | (defun shr-get-image-data (url) | 1150 | (defun shr-get-image-data (url) |
| 1162 | "Get image data for URL. | 1151 | "Get image data for URL. |
| @@ -1230,7 +1219,7 @@ START, and END. Note that START and END should be markers." | |||
| 1230 | (funcall shr-put-image-function | 1219 | (funcall shr-put-image-function |
| 1231 | image (buffer-substring start end)) | 1220 | image (buffer-substring start end)) |
| 1232 | (delete-region (point) end)))) | 1221 | (delete-region (point) end)))) |
| 1233 | (url-retrieve url 'shr-image-fetched | 1222 | (url-retrieve url #'shr-image-fetched |
| 1234 | (list (current-buffer) start end) | 1223 | (list (current-buffer) start end) |
| 1235 | t t))))) | 1224 | t t))))) |
| 1236 | 1225 | ||
| @@ -1679,7 +1668,7 @@ The preference is a float determined from `shr-prefer-media-type'." | |||
| 1679 | (or alt ""))) | 1668 | (or alt ""))) |
| 1680 | (insert " ") | 1669 | (insert " ") |
| 1681 | (url-queue-retrieve | 1670 | (url-queue-retrieve |
| 1682 | (shr-encode-url url) 'shr-image-fetched | 1671 | (shr-encode-url url) #'shr-image-fetched |
| 1683 | (list (current-buffer) start (set-marker (make-marker) (point)) | 1672 | (list (current-buffer) start (set-marker (make-marker) (point)) |
| 1684 | (list :width width :height height)) | 1673 | (list :width width :height height)) |
| 1685 | t | 1674 | t |
| @@ -2006,12 +1995,11 @@ BASE is the URL of the HTML being rendered." | |||
| 2006 | (cond | 1995 | (cond |
| 2007 | ((null tbodies) | 1996 | ((null tbodies) |
| 2008 | dom) | 1997 | dom) |
| 2009 | ((= (length tbodies) 1) | 1998 | ((null (cdr tbodies)) |
| 2010 | (car tbodies)) | 1999 | (car tbodies)) |
| 2011 | (t | 2000 | (t |
| 2012 | ;; Table with multiple tbodies. Convert into a single tbody. | 2001 | ;; Table with multiple tbodies. Convert into a single tbody. |
| 2013 | `(tbody nil ,@(cl-reduce 'append | 2002 | `(tbody nil ,@(mapcan #'dom-non-text-children tbodies)))))) |
| 2014 | (mapcar 'dom-non-text-children tbodies))))))) | ||
| 2015 | 2003 | ||
| 2016 | (defun shr--fix-tbody (tbody) | 2004 | (defun shr--fix-tbody (tbody) |
| 2017 | (nconc (list 'tbody (dom-attributes tbody)) | 2005 | (nconc (list 'tbody (dom-attributes tbody)) |
| @@ -2311,8 +2299,8 @@ flags that control whether to collect or render objects." | |||
| 2311 | (dolist (column row) | 2299 | (dolist (column row) |
| 2312 | (aset natural-widths i (max (aref natural-widths i) column)) | 2300 | (aset natural-widths i (max (aref natural-widths i) column)) |
| 2313 | (setq i (1+ i))))) | 2301 | (setq i (1+ i))))) |
| 2314 | (let ((extra (- (apply '+ (append suggested-widths nil)) | 2302 | (let ((extra (- (apply #'+ (append suggested-widths nil)) |
| 2315 | (apply '+ (append widths nil)) | 2303 | (apply #'+ (append widths nil)) |
| 2316 | (* shr-table-separator-pixel-width (1+ (length widths))))) | 2304 | (* shr-table-separator-pixel-width (1+ (length widths))))) |
| 2317 | (expanded-columns 0)) | 2305 | (expanded-columns 0)) |
| 2318 | ;; We have extra, unused space, so divide this space amongst the | 2306 | ;; We have extra, unused space, so divide this space amongst the |
diff --git a/lisp/net/tramp-archive.el b/lisp/net/tramp-archive.el index 95cbfb8c22a..24ee6fa51f3 100644 --- a/lisp/net/tramp-archive.el +++ b/lisp/net/tramp-archive.el | |||
| @@ -109,7 +109,7 @@ | |||
| 109 | 109 | ||
| 110 | (eval-when-compile (require 'cl-lib)) | 110 | (eval-when-compile (require 'cl-lib)) |
| 111 | ;; Sometimes, compilation fails with "Variable binding depth exceeds | 111 | ;; Sometimes, compilation fails with "Variable binding depth exceeds |
| 112 | ;; max-specpdl-size". | 112 | ;; max-specpdl-size". Shall be fixed in Emacs 27. |
| 113 | (eval-and-compile | 113 | (eval-and-compile |
| 114 | (let ((max-specpdl-size (* 2 max-specpdl-size))) (require 'tramp-gvfs))) | 114 | (let ((max-specpdl-size (* 2 max-specpdl-size))) (require 'tramp-gvfs))) |
| 115 | 115 | ||
| @@ -318,7 +318,10 @@ arguments to pass to the OPERATION." | |||
| 318 | 318 | ||
| 319 | (let* ((filename (apply #'tramp-archive-file-name-for-operation | 319 | (let* ((filename (apply #'tramp-archive-file-name-for-operation |
| 320 | operation args)) | 320 | operation args)) |
| 321 | (archive (tramp-archive-file-name-archive filename))) | 321 | (archive (tramp-archive-file-name-archive filename)) |
| 322 | ;; Sometimes, it fails with "Variable binding depth exceeds | ||
| 323 | ;; max-specpdl-size". Shall be fixed in Emacs 27. | ||
| 324 | (max-specpdl-size (* 2 max-specpdl-size))) | ||
| 322 | 325 | ||
| 323 | ;; `filename' could be a quoted file name. Or the file | 326 | ;; `filename' could be a quoted file name. Or the file |
| 324 | ;; archive could be a directory, see Bug#30293. | 327 | ;; archive could be a directory, see Bug#30293. |
diff --git a/lisp/net/tramp-rclone.el b/lisp/net/tramp-rclone.el index 445098a5bca..08bba33afed 100644 --- a/lisp/net/tramp-rclone.el +++ b/lisp/net/tramp-rclone.el | |||
| @@ -477,7 +477,18 @@ file names." | |||
| 477 | (with-tramp-connection-property | 477 | (with-tramp-connection-property |
| 478 | (tramp-get-connection-process vec) "rclone-pid" | 478 | (tramp-get-connection-process vec) "rclone-pid" |
| 479 | (catch 'pid | 479 | (catch 'pid |
| 480 | (dolist (pid (list-system-processes)) ;; "pidof rclone" ? | 480 | (dolist |
| 481 | (pid | ||
| 482 | ;; Until Emacs 25, `process-attributes' could | ||
| 483 | ;; crash Emacs for some processes. So we use | ||
| 484 | ;; "pidof", which might not work everywhere. | ||
| 485 | (if (<= emacs-major-version 25) | ||
| 486 | (let ((default-directory temporary-file-directory)) | ||
| 487 | (mapcar | ||
| 488 | #'string-to-number | ||
| 489 | (split-string | ||
| 490 | (shell-command-to-string "pidof rclone")))) | ||
| 491 | (list-system-processes))) | ||
| 481 | (and (string-match-p | 492 | (and (string-match-p |
| 482 | (regexp-quote | 493 | (regexp-quote |
| 483 | (format "rclone mount %s:" (tramp-file-name-host vec))) | 494 | (format "rclone mount %s:" (tramp-file-name-host vec))) |
diff --git a/lisp/net/webjump.el b/lisp/net/webjump.el index 6edd03c39cc..8bb156199c5 100644 --- a/lisp/net/webjump.el +++ b/lisp/net/webjump.el | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | ;;; webjump.el --- programmable Web hotlist | 1 | ;;; webjump.el --- programmable Web hotlist -*- lexical-binding: t; -*- |
| 2 | 2 | ||
| 3 | ;; Copyright (C) 1996-1997, 2001-2020 Free Software Foundation, Inc. | 3 | ;; Copyright (C) 1996-1997, 2001-2020 Free Software Foundation, Inc. |
| 4 | 4 | ||
| @@ -323,8 +323,7 @@ Please submit bug reports and other feedback to the author, Neil W. Van Dyke | |||
| 323 | 323 | ||
| 324 | (defun webjump-read-url-choice (what urls &optional default) | 324 | (defun webjump-read-url-choice (what urls &optional default) |
| 325 | ;; Note: Convert this to use `webjump-read-choice' someday. | 325 | ;; Note: Convert this to use `webjump-read-choice' someday. |
| 326 | (let* ((completions (mapcar (function (lambda (n) (cons n n))) | 326 | (let* ((completions (mapcar (lambda (n) (cons n n)) urls)) |
| 327 | urls)) | ||
| 328 | (input (completing-read (concat what | 327 | (input (completing-read (concat what |
| 329 | ;;(if default " (RET for default)" "") | 328 | ;;(if default " (RET for default)" "") |
| 330 | ": ") | 329 | ": ") |
diff --git a/lisp/obsolete/levents.el b/lisp/obsolete/levents.el deleted file mode 100644 index 2ae1ca48d16..00000000000 --- a/lisp/obsolete/levents.el +++ /dev/null | |||
| @@ -1,292 +0,0 @@ | |||
| 1 | ;;; levents.el --- emulate the Lucid event data type and associated functions | ||
| 2 | |||
| 3 | ;; Copyright (C) 1993, 2001-2020 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Maintainer: emacs-devel@gnu.org | ||
| 6 | ;; Keywords: emulations | ||
| 7 | ;; Obsolete-since: 23.2 | ||
| 8 | |||
| 9 | ;; This file is part of GNU Emacs. | ||
| 10 | |||
| 11 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 12 | ;; it under the terms of the GNU General Public License as published by | ||
| 13 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 14 | ;; (at your option) any later version. | ||
| 15 | |||
| 16 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | ;; GNU General Public License for more details. | ||
| 20 | |||
| 21 | ;; You should have received a copy of the GNU General Public License | ||
| 22 | ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. | ||
| 23 | |||
| 24 | ;;; Commentary: | ||
| 25 | |||
| 26 | ;; Things we cannot emulate in Lisp: | ||
| 27 | ;; It is not possible to emulate current-mouse-event as a variable, | ||
| 28 | ;; though it is not hard to obtain the data from (this-command-keys). | ||
| 29 | |||
| 30 | ;; We do not have a variable unread-command-event; | ||
| 31 | ;; instead, we have the more general unread-command-events. | ||
| 32 | |||
| 33 | ;; Our read-key-sequence and read-char are not precisely | ||
| 34 | ;; compatible with those in Lucid Emacs, but they should work ok. | ||
| 35 | |||
| 36 | ;;; Code: | ||
| 37 | |||
| 38 | (defun next-command-event (event) | ||
| 39 | (error "You must rewrite to use `read-command-event' instead of `next-command-event'")) | ||
| 40 | |||
| 41 | (defun next-event (event) | ||
| 42 | (error "You must rewrite to use `read-event' instead of `next-event'")) | ||
| 43 | |||
| 44 | (defun dispatch-event (event) | ||
| 45 | (error "`dispatch-event' not supported")) | ||
| 46 | |||
| 47 | ;; Make events of type eval, menu and timeout | ||
| 48 | ;; execute properly. | ||
| 49 | |||
| 50 | (define-key global-map [menu] 'execute-eval-event) | ||
| 51 | (define-key global-map [timeout] 'execute-eval-event) | ||
| 52 | (define-key global-map [eval] 'execute-eval-event) | ||
| 53 | |||
| 54 | (defun execute-eval-event (event) | ||
| 55 | (interactive "e") | ||
| 56 | (funcall (nth 1 event) (nth 2 event))) | ||
| 57 | |||
| 58 | (put 'eval 'event-symbol-elements '(eval)) | ||
| 59 | (put 'menu 'event-symbol-elements '(eval)) | ||
| 60 | (put 'timeout 'event-symbol-elements '(eval)) | ||
| 61 | |||
| 62 | (defun allocate-event () | ||
| 63 | "Return an empty event structure. | ||
| 64 | In this emulation, it returns nil." | ||
| 65 | nil) | ||
| 66 | |||
| 67 | (defun button-press-event-p (obj) | ||
| 68 | "True if the argument is a mouse-button-press event object." | ||
| 69 | (and (consp obj) (symbolp (car obj)) | ||
| 70 | (memq 'down (get (car obj) 'event-symbol-elements)))) | ||
| 71 | |||
| 72 | (defun button-release-event-p (obj) | ||
| 73 | "True if the argument is a mouse-button-release event object." | ||
| 74 | (and (consp obj) (symbolp (car obj)) | ||
| 75 | (or (memq 'click (get (car obj) 'event-symbol-elements)) | ||
| 76 | (memq 'drag (get (car obj) 'event-symbol-elements))))) | ||
| 77 | |||
| 78 | (defun button-event-p (obj) | ||
| 79 | "True if the argument is a mouse-button press or release event object." | ||
| 80 | (and (consp obj) (symbolp (car obj)) | ||
| 81 | (or (memq 'click (get (car obj) 'event-symbol-elements)) | ||
| 82 | (memq 'down (get (car obj) 'event-symbol-elements)) | ||
| 83 | (memq 'drag (get (car obj) 'event-symbol-elements))))) | ||
| 84 | |||
| 85 | (defun mouse-event-p (obj) | ||
| 86 | "True if the argument is a mouse-button press or release event object." | ||
| 87 | (and (consp obj) (symbolp (car obj)) | ||
| 88 | (or (eq (car obj) 'mouse-movement) | ||
| 89 | (memq 'click (get (car obj) 'event-symbol-elements)) | ||
| 90 | (memq 'down (get (car obj) 'event-symbol-elements)) | ||
| 91 | (memq 'drag (get (car obj) 'event-symbol-elements))))) | ||
| 92 | |||
| 93 | (defun character-to-event (ch &optional event) | ||
| 94 | "Converts a numeric ASCII value to an event structure, replete with | ||
| 95 | bucky bits. The character is the first argument, and the event to fill | ||
| 96 | in is the second. This function contains knowledge about what the codes | ||
| 97 | mean -- for example, the number 9 is converted to the character Tab, | ||
| 98 | not the distinct character Control-I. | ||
| 99 | |||
| 100 | Beware that character-to-event and event-to-character are not strictly | ||
| 101 | inverse functions, since events contain much more information than the | ||
| 102 | ASCII character set can encode." | ||
| 103 | ch) | ||
| 104 | |||
| 105 | (defun copy-event (event1 &optional event2) | ||
| 106 | "Make a copy of the given event object. | ||
| 107 | In this emulation, `copy-event' just returns its argument." | ||
| 108 | event1) | ||
| 109 | |||
| 110 | (defun deallocate-event (event) | ||
| 111 | "Allow the given event structure to be reused. | ||
| 112 | In actual Lucid Emacs, you MUST NOT use this event object after | ||
| 113 | calling this function with it. You will lose. It is not necessary to | ||
| 114 | call this function, as event objects are garbage- collected like all | ||
| 115 | other objects; however, it may be more efficient to explicitly | ||
| 116 | deallocate events when you are sure that this is safe. | ||
| 117 | |||
| 118 | This emulation does not actually deallocate or reuse events | ||
| 119 | except via garbage collection and `cons'." | ||
| 120 | nil) | ||
| 121 | |||
| 122 | (defun enqueue-eval-event: (function object) | ||
| 123 | "Add an eval event to the back of the queue. | ||
| 124 | It will be the next event read after all pending events." | ||
| 125 | (setq unread-command-events | ||
| 126 | (nconc unread-command-events | ||
| 127 | (list (list 'eval function object))))) | ||
| 128 | |||
| 129 | (defun eval-event-p (obj) | ||
| 130 | "True if the argument is an eval or menu event object." | ||
| 131 | (eq (car-safe obj) 'eval)) | ||
| 132 | |||
| 133 | (defun event-button (event) | ||
| 134 | "Return the button-number of the given mouse-button-press event." | ||
| 135 | (let ((sym (car (get (car event) 'event-symbol-elements)))) | ||
| 136 | (cdr (assq sym '((mouse-1 . 1) (mouse-2 . 2) (mouse-3 . 3) | ||
| 137 | (mouse-4 . 4) (mouse-5 . 5)))))) | ||
| 138 | |||
| 139 | (defun event-function (event) | ||
| 140 | "Return the callback function of the given timeout, menu, or eval event." | ||
| 141 | (nth 1 event)) | ||
| 142 | |||
| 143 | (defun event-key (event) | ||
| 144 | "Return the KeySym of the given key-press event. | ||
| 145 | The value is an ASCII printing character (not upper case) or a symbol." | ||
| 146 | (if (symbolp event) | ||
| 147 | (car (get event 'event-symbol-elements)) | ||
| 148 | (let ((base (logand event (1- (ash 1 18))))) | ||
| 149 | (downcase (if (< base 32) (logior base 64) base))))) | ||
| 150 | |||
| 151 | (defun event-object (event) | ||
| 152 | "Return the function argument of the given timeout, menu, or eval event." | ||
| 153 | (nth 2 event)) | ||
| 154 | |||
| 155 | (defun event-point (event) | ||
| 156 | "Return the character position of the given mouse-related event. | ||
| 157 | If the event did not occur over a window, or did | ||
| 158 | not occur over text, then this returns nil. Otherwise, it returns an index | ||
| 159 | into the buffer visible in the event's window." | ||
| 160 | (posn-point (event-end event))) | ||
| 161 | |||
| 162 | ;; Return position of start of line LINE in WINDOW. | ||
| 163 | ;; If LINE is nil, return the last position | ||
| 164 | ;; visible in WINDOW. | ||
| 165 | (defun event-closest-point-1 (window &optional line) | ||
| 166 | (let* ((total (- (window-height window) | ||
| 167 | (if (window-minibuffer-p window) | ||
| 168 | 0 1))) | ||
| 169 | (distance (or line total))) | ||
| 170 | (save-excursion | ||
| 171 | (goto-char (window-start window)) | ||
| 172 | (if (= (vertical-motion distance) distance) | ||
| 173 | (if (not line) | ||
| 174 | (forward-char -1))) | ||
| 175 | (point)))) | ||
| 176 | |||
| 177 | (defun event-closest-point (event &optional start-window) | ||
| 178 | "Return the nearest position to where EVENT ended its motion. | ||
| 179 | This is computed for the window where EVENT's motion started, | ||
| 180 | or for window WINDOW if that is specified." | ||
| 181 | (or start-window (setq start-window (posn-window (event-start event)))) | ||
| 182 | (if (eq start-window (posn-window (event-end event))) | ||
| 183 | (if (eq (event-point event) 'vertical-line) | ||
| 184 | (event-closest-point-1 start-window | ||
| 185 | (cdr (posn-col-row (event-end event)))) | ||
| 186 | (if (eq (event-point event) 'mode-line) | ||
| 187 | (event-closest-point-1 start-window) | ||
| 188 | (event-point event))) | ||
| 189 | ;; EVENT ended in some other window. | ||
| 190 | (let* ((end-w (posn-window (event-end event))) | ||
| 191 | (end-w-top) | ||
| 192 | (w-top (nth 1 (window-edges start-window)))) | ||
| 193 | (setq end-w-top | ||
| 194 | (if (windowp end-w) | ||
| 195 | (nth 1 (window-edges end-w)) | ||
| 196 | (/ (cdr (posn-x-y (event-end event))) | ||
| 197 | (frame-char-height end-w)))) | ||
| 198 | (if (>= end-w-top w-top) | ||
| 199 | (event-closest-point-1 start-window) | ||
| 200 | (window-start start-window))))) | ||
| 201 | |||
| 202 | (defun event-process (event) | ||
| 203 | "Return the process of the given process-output event." | ||
| 204 | (nth 1 event)) | ||
| 205 | |||
| 206 | (defun event-timestamp (event) | ||
| 207 | "Return the timestamp of the given event object. | ||
| 208 | In Lucid Emacs, this works for any kind of event. | ||
| 209 | In this emulation, it returns nil for non-mouse-related events." | ||
| 210 | (and (listp event) | ||
| 211 | (posn-timestamp (event-end event)))) | ||
| 212 | |||
| 213 | (defun event-to-character (event &optional lenient) | ||
| 214 | "Return the closest ASCII approximation to the given event object. | ||
| 215 | If the event isn't a keypress, this returns nil. | ||
| 216 | If the second argument is non-nil, then this is lenient in its | ||
| 217 | translation; it will ignore modifier keys other than control and meta, | ||
| 218 | and will ignore the shift modifier on those characters which have no | ||
| 219 | shifted ASCII equivalent (Control-Shift-A for example, will be mapped to | ||
| 220 | the same ASCII code as Control-A.) If the second arg is nil, then nil | ||
| 221 | will be returned for events which have no direct ASCII equivalent." | ||
| 222 | (if (symbolp event) | ||
| 223 | (and lenient | ||
| 224 | (cdr (assq event '((backspace . 8) (delete . 127) (tab . 9) | ||
| 225 | (return . 10) (enter . 10))))) | ||
| 226 | ;; Our interpretation is, ASCII means anything a number can represent. | ||
| 227 | (if (integerp event) | ||
| 228 | event nil))) | ||
| 229 | |||
| 230 | (defun event-window (event) | ||
| 231 | "Return the window of the given mouse-related event object." | ||
| 232 | (posn-window (event-end event))) | ||
| 233 | |||
| 234 | (defun event-x (event) | ||
| 235 | "Return the X position in characters of the given mouse-related event." | ||
| 236 | (/ (car (posn-col-row (event-end event))) | ||
| 237 | (frame-char-width (window-frame (event-window event))))) | ||
| 238 | |||
| 239 | (defun event-x-pixel (event) | ||
| 240 | "Return the X position in pixels of the given mouse-related event." | ||
| 241 | (car (posn-col-row (event-end event)))) | ||
| 242 | |||
| 243 | (defun event-y (event) | ||
| 244 | "Return the Y position in characters of the given mouse-related event." | ||
| 245 | (/ (cdr (posn-col-row (event-end event))) | ||
| 246 | (frame-char-height (window-frame (event-window event))))) | ||
| 247 | |||
| 248 | (defun event-y-pixel (event) | ||
| 249 | "Return the Y position in pixels of the given mouse-related event." | ||
| 250 | (cdr (posn-col-row (event-end event)))) | ||
| 251 | |||
| 252 | (defun key-press-event-p (obj) | ||
| 253 | "True if the argument is a keyboard event object." | ||
| 254 | (or (integerp obj) | ||
| 255 | (and (symbolp obj) | ||
| 256 | (get obj 'event-symbol-elements)))) | ||
| 257 | |||
| 258 | (defun menu-event-p (obj) | ||
| 259 | "True if the argument is a menu event object." | ||
| 260 | (eq (car-safe obj) 'menu)) | ||
| 261 | |||
| 262 | (defun motion-event-p (obj) | ||
| 263 | "True if the argument is a mouse-motion event object." | ||
| 264 | (eq (car-safe obj) 'mouse-movement)) | ||
| 265 | |||
| 266 | (defun read-command-event () | ||
| 267 | "Return the next keyboard or mouse event; execute other events. | ||
| 268 | This is similar to the function `next-command-event' of Lucid Emacs, | ||
| 269 | but different in that it returns the event rather than filling in | ||
| 270 | an existing event object." | ||
| 271 | (let (event) | ||
| 272 | (while (progn | ||
| 273 | (setq event (read-event)) | ||
| 274 | (not (or (key-press-event-p event) | ||
| 275 | (button-press-event-p event) | ||
| 276 | (button-release-event-p event) | ||
| 277 | (menu-event-p event)))) | ||
| 278 | (let ((type (car-safe event))) | ||
| 279 | (cond ((eq type 'eval) | ||
| 280 | (funcall (nth 1 event) (nth 2 event))) | ||
| 281 | ((eq type 'switch-frame) | ||
| 282 | (select-frame (nth 1 event)))))) | ||
| 283 | event)) | ||
| 284 | |||
| 285 | (defun process-event-p (obj) | ||
| 286 | "True if the argument is a process-output event object. | ||
| 287 | GNU Emacs 19 does not currently generate process-output events." | ||
| 288 | (eq (car-safe obj) 'process)) | ||
| 289 | |||
| 290 | (provide 'levents) | ||
| 291 | |||
| 292 | ;;; levents.el ends here | ||
diff --git a/lisp/org/org-agenda.el b/lisp/org/org-agenda.el index 5fe140d00ef..689d134627e 100644 --- a/lisp/org/org-agenda.el +++ b/lisp/org/org-agenda.el | |||
| @@ -2995,7 +2995,8 @@ Agenda views are separated by `org-agenda-block-separator'." | |||
| 2995 | (erase-buffer) | 2995 | (erase-buffer) |
| 2996 | (insert (eval-when-compile | 2996 | (insert (eval-when-compile |
| 2997 | (let ((header | 2997 | (let ((header |
| 2998 | "Press key for an agenda command: | 2998 | (copy-sequence |
| 2999 | "Press key for an agenda command: | ||
| 2999 | -------------------------------- < Buffer, subtree/region restriction | 3000 | -------------------------------- < Buffer, subtree/region restriction |
| 3000 | a Agenda for current week or day > Remove restriction | 3001 | a Agenda for current week or day > Remove restriction |
| 3001 | t List of all TODO entries e Export agenda views | 3002 | t List of all TODO entries e Export agenda views |
| @@ -3004,7 +3005,7 @@ s Search for keywords M Like m, but only TODO entries | |||
| 3004 | / Multi-occur S Like s, but only TODO entries | 3005 | / Multi-occur S Like s, but only TODO entries |
| 3005 | ? Find :FLAGGED: entries C Configure custom agenda commands | 3006 | ? Find :FLAGGED: entries C Configure custom agenda commands |
| 3006 | * Toggle sticky agenda views # List stuck projects (!=configure) | 3007 | * Toggle sticky agenda views # List stuck projects (!=configure) |
| 3007 | ") | 3008 | ")) |
| 3008 | (start 0)) | 3009 | (start 0)) |
| 3009 | (while (string-match | 3010 | (while (string-match |
| 3010 | "\\(^\\| \\|(\\)\\(\\S-\\)\\( \\|=\\)" | 3011 | "\\(^\\| \\|(\\)\\(\\S-\\)\\( \\|=\\)" |
diff --git a/lisp/password-cache.el b/lisp/password-cache.el index 86d802f283c..f5007579a8a 100644 --- a/lisp/password-cache.el +++ b/lisp/password-cache.el | |||
| @@ -31,7 +31,8 @@ | |||
| 31 | ;; ;; Minibuffer prompt for password. | 31 | ;; ;; Minibuffer prompt for password. |
| 32 | ;; => "foo" | 32 | ;; => "foo" |
| 33 | ;; | 33 | ;; |
| 34 | ;; (password-cache-add "test" (copy-sequence "foo")) | 34 | ;; (password-cache-add "test" (read-passwd "Password? ")) |
| 35 | ;; ;; Minibuffer prompt from read-passwd, which returns "foo". | ||
| 35 | ;; => nil | 36 | ;; => nil |
| 36 | 37 | ||
| 37 | ;; (password-read "Password? " "test") | 38 | ;; (password-read "Password? " "test") |
diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el index 1e72352f719..17ffea59ff0 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el | |||
| @@ -3412,8 +3412,14 @@ regexp should match \"(\" if parentheses are valid in declarators. | |||
| 3412 | The end of the first submatch is taken as the end of the operator. | 3412 | The end of the first submatch is taken as the end of the operator. |
| 3413 | Identifier syntax is in effect when this is matched (see | 3413 | Identifier syntax is in effect when this is matched (see |
| 3414 | `c-identifier-syntax-table')." | 3414 | `c-identifier-syntax-table')." |
| 3415 | t (if (c-lang-const c-type-modifier-kwds) | 3415 | t (if (or (c-lang-const c-type-modifier-kwds) (c-lang-const c-modifier-kwds)) |
| 3416 | (concat (regexp-opt (c-lang-const c-type-modifier-kwds) t) "\\>") | 3416 | (concat |
| 3417 | (regexp-opt (c--delete-duplicates | ||
| 3418 | (append (c-lang-const c-type-modifier-kwds) | ||
| 3419 | (c-lang-const c-modifier-kwds)) | ||
| 3420 | :test 'string-equal) | ||
| 3421 | t) | ||
| 3422 | "\\>") | ||
| 3417 | ;; Default to a regexp that never matches. | 3423 | ;; Default to a regexp that never matches. |
| 3418 | regexp-unmatchable) | 3424 | regexp-unmatchable) |
| 3419 | ;; Check that there's no "=" afterwards to avoid matching tokens | 3425 | ;; Check that there's no "=" afterwards to avoid matching tokens |
diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el index d822788bee2..b3b2374805d 100644 --- a/lisp/progmodes/cc-mode.el +++ b/lisp/progmodes/cc-mode.el | |||
| @@ -795,7 +795,7 @@ compatible with old code; callers should always specify it." | |||
| 795 | (set (make-local-variable 'outline-level) 'c-outline-level) | 795 | (set (make-local-variable 'outline-level) 'c-outline-level) |
| 796 | (set (make-local-variable 'add-log-current-defun-function) | 796 | (set (make-local-variable 'add-log-current-defun-function) |
| 797 | (lambda () | 797 | (lambda () |
| 798 | (or (c-cpp-define-name) (c-defun-name)))) | 798 | (or (c-cpp-define-name) (car (c-defun-name-and-limits nil))))) |
| 799 | (let ((rfn (assq mode c-require-final-newline))) | 799 | (let ((rfn (assq mode c-require-final-newline))) |
| 800 | (when rfn | 800 | (when rfn |
| 801 | (if (boundp 'mode-require-final-newline) | 801 | (if (boundp 'mode-require-final-newline) |
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index 198f040fb29..c72e9d94b1c 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | ;;; project.el --- Operations on the current project -*- lexical-binding: t; -*- | 1 | ;;; project.el --- Operations on the current project -*- lexical-binding: t; -*- |
| 2 | 2 | ||
| 3 | ;; Copyright (C) 2015-2020 Free Software Foundation, Inc. | 3 | ;; Copyright (C) 2015-2020 Free Software Foundation, Inc. |
| 4 | ;; Version: 0.1.3 | 4 | ;; Version: 0.2.0 |
| 5 | ;; Package-Requires: ((emacs "26.3")) | 5 | ;; Package-Requires: ((emacs "26.3")) |
| 6 | 6 | ||
| 7 | ;; This is a GNU ELPA :core package. Avoid using functionality that | 7 | ;; This is a GNU ELPA :core package. Avoid using functionality that |
| @@ -40,7 +40,7 @@ | |||
| 40 | ;; Infrastructure: | 40 | ;; Infrastructure: |
| 41 | ;; | 41 | ;; |
| 42 | ;; Function `project-current', to determine the current project | 42 | ;; Function `project-current', to determine the current project |
| 43 | ;; instance, and 5 (at the moment) generic functions that act on it. | 43 | ;; instance, and 4 (at the moment) generic functions that act on it. |
| 44 | ;; This list is to be extended in future versions. | 44 | ;; This list is to be extended in future versions. |
| 45 | ;; | 45 | ;; |
| 46 | ;; Utils: | 46 | ;; Utils: |
| @@ -122,14 +122,25 @@ is not a part of a detectable project either, return a | |||
| 122 | (defun project--find-in-directory (dir) | 122 | (defun project--find-in-directory (dir) |
| 123 | (run-hook-with-args-until-success 'project-find-functions dir)) | 123 | (run-hook-with-args-until-success 'project-find-functions dir)) |
| 124 | 124 | ||
| 125 | (cl-defgeneric project-roots (project) | 125 | (cl-defgeneric project-root (project) |
| 126 | "Return the list of directory roots of the current project. | 126 | "Return root directory of the current project. |
| 127 | |||
| 128 | It usually contains the main build file, dependencies | ||
| 129 | configuration file, etc. Though neither is mandatory. | ||
| 127 | 130 | ||
| 128 | Most often it's just one directory which contains the project | 131 | The directory name must be absolute." |
| 129 | build file and everything else in the project. But in more | 132 | (car (project-roots project))) |
| 130 | advanced configurations, a project can span multiple directories. | ||
| 131 | 133 | ||
| 132 | The directory names should be absolute.") | 134 | (cl-defgeneric project-roots (project) |
| 135 | "Return the list containing the current project root. | ||
| 136 | |||
| 137 | The function is obsolete, all projects have one main root anyway, | ||
| 138 | and the rest should be possible to express through | ||
| 139 | `project-external-roots'." | ||
| 140 | ;; FIXME: Can we specify project's version here? | ||
| 141 | ;; FIXME: Could we make this affect cl-defmethod calls too? | ||
| 142 | (declare (obsolete project-root "0.3.0")) | ||
| 143 | (list (project-root project))) | ||
| 133 | 144 | ||
| 134 | ;; FIXME: Add MODE argument, like in `ede-source-paths'? | 145 | ;; FIXME: Add MODE argument, like in `ede-source-paths'? |
| 135 | (cl-defgeneric project-external-roots (_project) | 146 | (cl-defgeneric project-external-roots (_project) |
| @@ -138,18 +149,14 @@ The directory names should be absolute.") | |||
| 138 | It's the list of directories outside of the project that are | 149 | It's the list of directories outside of the project that are |
| 139 | still related to it. If the project deals with source code then, | 150 | still related to it. If the project deals with source code then, |
| 140 | depending on the languages used, this list should include the | 151 | depending on the languages used, this list should include the |
| 141 | headers search path, load path, class path, and so on. | 152 | headers search path, load path, class path, and so on." |
| 142 | |||
| 143 | The rule of thumb for whether to include a directory here, and | ||
| 144 | not in `project-roots', is whether its contents are meant to be | ||
| 145 | edited together with the rest of the project." | ||
| 146 | nil) | 153 | nil) |
| 147 | 154 | ||
| 148 | (cl-defgeneric project-ignores (_project _dir) | 155 | (cl-defgeneric project-ignores (_project _dir) |
| 149 | "Return the list of glob patterns to ignore inside DIR. | 156 | "Return the list of glob patterns to ignore inside DIR. |
| 150 | Patterns can match both regular files and directories. | 157 | Patterns can match both regular files and directories. |
| 151 | To root an entry, start it with `./'. To match directories only, | 158 | To root an entry, start it with `./'. To match directories only, |
| 152 | end it with `/'. DIR must be one of `project-roots' or | 159 | end it with `/'. DIR must be either `project-root' or one of |
| 153 | `project-external-roots'." | 160 | `project-external-roots'." |
| 154 | ;; TODO: Document and support regexp ignores as used by Hg. | 161 | ;; TODO: Document and support regexp ignores as used by Hg. |
| 155 | ;; TODO: Support whitelist entries. | 162 | ;; TODO: Support whitelist entries. |
| @@ -170,13 +177,13 @@ end it with `/'. DIR must be one of `project-roots' or | |||
| 170 | (t | 177 | (t |
| 171 | (complete-with-action action all-files string pred))))) | 178 | (complete-with-action action all-files string pred))))) |
| 172 | 179 | ||
| 173 | (cl-defmethod project-roots ((project (head transient))) | 180 | (cl-defmethod project-root ((project (head transient))) |
| 174 | (list (cdr project))) | 181 | (cdr project)) |
| 175 | 182 | ||
| 176 | (cl-defgeneric project-files (project &optional dirs) | 183 | (cl-defgeneric project-files (project &optional dirs) |
| 177 | "Return a list of files in directories DIRS in PROJECT. | 184 | "Return a list of files in directories DIRS in PROJECT. |
| 178 | DIRS is a list of absolute directories; it should be some | 185 | DIRS is a list of absolute directories; it should be some |
| 179 | subset of the project roots and external roots. | 186 | subset of the project root and external roots. |
| 180 | 187 | ||
| 181 | The default implementation uses `find-program'. PROJECT is used | 188 | The default implementation uses `find-program'. PROJECT is used |
| 182 | to find the list of ignores for each directory." | 189 | to find the list of ignores for each directory." |
| @@ -184,7 +191,8 @@ to find the list of ignores for each directory." | |||
| 184 | (lambda (dir) | 191 | (lambda (dir) |
| 185 | (project--files-in-directory dir | 192 | (project--files-in-directory dir |
| 186 | (project--dir-ignores project dir))) | 193 | (project--dir-ignores project dir))) |
| 187 | (or dirs (project-roots project)))) | 194 | (or dirs |
| 195 | (list (project-root project))))) | ||
| 188 | 196 | ||
| 189 | (defun project--files-in-directory (dir ignores &optional files) | 197 | (defun project--files-in-directory (dir ignores &optional files) |
| 190 | (require 'find-dired) | 198 | (require 'find-dired) |
| @@ -223,7 +231,7 @@ to find the list of ignores for each directory." | |||
| 223 | local-files)))) | 231 | local-files)))) |
| 224 | 232 | ||
| 225 | (defgroup project-vc nil | 233 | (defgroup project-vc nil |
| 226 | "Project implementation using the VC package." | 234 | "Project implementation based on the VC package." |
| 227 | :version "25.1" | 235 | :version "25.1" |
| 228 | :group 'tools) | 236 | :group 'tools) |
| 229 | 237 | ||
| @@ -232,6 +240,15 @@ to find the list of ignores for each directory." | |||
| 232 | :type '(repeat string) | 240 | :type '(repeat string) |
| 233 | :safe 'listp) | 241 | :safe 'listp) |
| 234 | 242 | ||
| 243 | (defcustom project-vc-merge-submodules t | ||
| 244 | "Non-nil to consider submodules part of the parent project. | ||
| 245 | |||
| 246 | After changing this variable (using Customize or .dir-locals.el) | ||
| 247 | you might have to restart Emacs to see the effect." | ||
| 248 | :type 'boolean | ||
| 249 | :package-version '(project . "0.2.0") | ||
| 250 | :safe 'booleanp) | ||
| 251 | |||
| 235 | ;; FIXME: Using the current approach, major modes are supposed to set | 252 | ;; FIXME: Using the current approach, major modes are supposed to set |
| 236 | ;; this variable to a buffer-local value. So we don't have access to | 253 | ;; this variable to a buffer-local value. So we don't have access to |
| 237 | ;; the "external roots" of language A from buffers of language B, which | 254 | ;; the "external roots" of language A from buffers of language B, which |
| @@ -273,38 +290,48 @@ backend implementation of `project-external-roots'.") | |||
| 273 | (pcase backend | 290 | (pcase backend |
| 274 | ('Git | 291 | ('Git |
| 275 | ;; Don't stop at submodule boundary. | 292 | ;; Don't stop at submodule boundary. |
| 276 | ;; Note: It's not necessarily clear-cut what should be | ||
| 277 | ;; considered a "submodule" in the sense that some users | ||
| 278 | ;; may setup things equivalent to "git-submodule"s using | ||
| 279 | ;; "git worktree" instead (for example). | ||
| 280 | ;; FIXME: Also it may be the case that some users would consider | ||
| 281 | ;; a submodule as its own project. So there's a good chance | ||
| 282 | ;; we will need to let the user tell us what is their intention. | ||
| 283 | (or (vc-file-getprop dir 'project-git-root) | 293 | (or (vc-file-getprop dir 'project-git-root) |
| 284 | (let* ((root (vc-call-backend backend 'root dir)) | 294 | (let ((root (vc-call-backend backend 'root dir))) |
| 285 | (gitfile (expand-file-name ".git" root))) | ||
| 286 | (vc-file-setprop | 295 | (vc-file-setprop |
| 287 | dir 'project-git-root | 296 | dir 'project-git-root |
| 288 | (cond | 297 | (if (and |
| 289 | ((file-directory-p gitfile) | 298 | ;; FIXME: Invalidate the cache when the value |
| 290 | root) | 299 | ;; of this variable changes. |
| 291 | ((with-temp-buffer | 300 | project-vc-merge-submodules |
| 292 | (insert-file-contents gitfile) | 301 | (project--submodule-p root)) |
| 293 | (goto-char (point-min)) | 302 | (let* ((parent (file-name-directory |
| 294 | ;; Kind of a hack to distinguish a submodule from | 303 | (directory-file-name root)))) |
| 295 | ;; other cases of .git files pointing elsewhere. | 304 | (vc-call-backend backend 'root parent)) |
| 296 | (looking-at "gitdir: [./]+/\\.git/modules/")) | 305 | root))))) |
| 297 | (let* ((parent (file-name-directory | ||
| 298 | (directory-file-name root)))) | ||
| 299 | (vc-call-backend backend 'root parent))) | ||
| 300 | (t root))) | ||
| 301 | ))) | ||
| 302 | ('nil nil) | 306 | ('nil nil) |
| 303 | (_ (ignore-errors (vc-call-backend backend 'root dir)))))) | 307 | (_ (ignore-errors (vc-call-backend backend 'root dir)))))) |
| 304 | (and root (cons 'vc root)))) | 308 | (and root (cons 'vc root)))) |
| 305 | 309 | ||
| 306 | (cl-defmethod project-roots ((project (head vc))) | 310 | (defun project--submodule-p (root) |
| 307 | (list (cdr project))) | 311 | ;; XXX: We only support Git submodules for now. |
| 312 | ;; | ||
| 313 | ;; For submodules, at least, we expect the users to prefer them to | ||
| 314 | ;; be considered part of the parent project. For those who don't, | ||
| 315 | ;; there is the custom var now. | ||
| 316 | ;; | ||
| 317 | ;; Some users may also set up things equivalent to Git submodules | ||
| 318 | ;; using "git worktree" (for example). However, we expect that most | ||
| 319 | ;; of them would prefer to treat those as separate projects anyway. | ||
| 320 | (let* ((gitfile (expand-file-name ".git" root))) | ||
| 321 | (cond | ||
| 322 | ((file-directory-p gitfile) | ||
| 323 | nil) | ||
| 324 | ((with-temp-buffer | ||
| 325 | (insert-file-contents gitfile) | ||
| 326 | (goto-char (point-min)) | ||
| 327 | ;; Kind of a hack to distinguish a submodule from | ||
| 328 | ;; other cases of .git files pointing elsewhere. | ||
| 329 | (looking-at "gitdir: [./]+/\\.git/modules/")) | ||
| 330 | t) | ||
| 331 | (t nil)))) | ||
| 332 | |||
| 333 | (cl-defmethod project-root ((project (head vc))) | ||
| 334 | (cdr project)) | ||
| 308 | 335 | ||
| 309 | (cl-defmethod project-external-roots ((project (head vc))) | 336 | (cl-defmethod project-external-roots ((project (head vc))) |
| 310 | (project-subtract-directories | 337 | (project-subtract-directories |
| @@ -312,7 +339,7 @@ backend implementation of `project-external-roots'.") | |||
| 312 | (mapcar | 339 | (mapcar |
| 313 | #'file-name-as-directory | 340 | #'file-name-as-directory |
| 314 | (funcall project-vc-external-roots-function))) | 341 | (funcall project-vc-external-roots-function))) |
| 315 | (project-roots project))) | 342 | (list (project-root project)))) |
| 316 | 343 | ||
| 317 | (cl-defmethod project-files ((project (head vc)) &optional dirs) | 344 | (cl-defmethod project-files ((project (head vc)) &optional dirs) |
| 318 | (cl-mapcan | 345 | (cl-mapcan |
| @@ -330,7 +357,8 @@ backend implementation of `project-external-roots'.") | |||
| 330 | (project--files-in-directory | 357 | (project--files-in-directory |
| 331 | dir | 358 | dir |
| 332 | (project--dir-ignores project dir))))) | 359 | (project--dir-ignores project dir))))) |
| 333 | (or dirs (project-roots project)))) | 360 | (or dirs |
| 361 | (list (project-root project))))) | ||
| 334 | 362 | ||
| 335 | (declare-function vc-git--program-version "vc-git") | 363 | (declare-function vc-git--program-version "vc-git") |
| 336 | (declare-function vc-git--run-command-string "vc-git") | 364 | (declare-function vc-git--run-command-string "vc-git") |
| @@ -372,7 +400,9 @@ backend implementation of `project-external-roots'.") | |||
| 372 | submodules))) | 400 | submodules))) |
| 373 | (setq files | 401 | (setq files |
| 374 | (apply #'nconc files sub-files))) | 402 | (apply #'nconc files sub-files))) |
| 375 | files)) | 403 | ;; 'git ls-files' returns duplicate entries for merge conflicts. |
| 404 | ;; XXX: Better solutions welcome, but this seems cheap enough. | ||
| 405 | (delete-consecutive-dups files))) | ||
| 376 | (`Hg | 406 | (`Hg |
| 377 | (let ((default-directory (expand-file-name (file-name-as-directory dir))) | 407 | (let ((default-directory (expand-file-name (file-name-as-directory dir))) |
| 378 | args) | 408 | args) |
| @@ -471,7 +501,7 @@ requires quoting, e.g. `\\[quoted-insert]<space>'." | |||
| 471 | (let* ((pr (project-current t)) | 501 | (let* ((pr (project-current t)) |
| 472 | (files | 502 | (files |
| 473 | (if (not current-prefix-arg) | 503 | (if (not current-prefix-arg) |
| 474 | (project-files pr (project-roots pr)) | 504 | (project-files pr) |
| 475 | (let ((dir (read-directory-name "Base directory: " | 505 | (let ((dir (read-directory-name "Base directory: " |
| 476 | nil default-directory t))) | 506 | nil default-directory t))) |
| 477 | (project--files-in-directory dir | 507 | (project--files-in-directory dir |
| @@ -482,9 +512,8 @@ requires quoting, e.g. `\\[quoted-insert]<space>'." | |||
| 482 | nil))) | 512 | nil))) |
| 483 | 513 | ||
| 484 | (defun project--dir-ignores (project dir) | 514 | (defun project--dir-ignores (project dir) |
| 485 | (let* ((roots (project-roots project)) | 515 | (let ((root (project-root project))) |
| 486 | (root (cl-find dir roots :test #'file-in-directory-p))) | 516 | (if (not (file-in-directory-p dir root)) |
| 487 | (if (not root) | ||
| 488 | (project-ignores nil nil) ;The defaults. | 517 | (project-ignores nil nil) ;The defaults. |
| 489 | (let ((ignores (project-ignores project root))) | 518 | (let ((ignores (project-ignores project root))) |
| 490 | (if (file-equal-p root dir) | 519 | (if (file-equal-p root dir) |
| @@ -502,8 +531,8 @@ pattern to search for." | |||
| 502 | (require 'xref) | 531 | (require 'xref) |
| 503 | (let* ((pr (project-current t)) | 532 | (let* ((pr (project-current t)) |
| 504 | (files | 533 | (files |
| 505 | (project-files pr (append | 534 | (project-files pr (cons |
| 506 | (project-roots pr) | 535 | (project-root pr) |
| 507 | (project-external-roots pr))))) | 536 | (project-external-roots pr))))) |
| 508 | (xref--show-xrefs | 537 | (xref--show-xrefs |
| 509 | (apply-partially #'project--find-regexp-in-files regexp files) | 538 | (apply-partially #'project--find-regexp-in-files regexp files) |
| @@ -541,23 +570,23 @@ pattern to search for." | |||
| 541 | 570 | ||
| 542 | ;;;###autoload | 571 | ;;;###autoload |
| 543 | (defun project-find-file () | 572 | (defun project-find-file () |
| 544 | "Visit a file (with completion) in the current project's roots. | 573 | "Visit a file (with completion) in the current project. |
| 545 | The completion default is the filename at point, if one is | 574 | The completion default is the filename at point, if one is |
| 546 | recognized." | 575 | recognized." |
| 547 | (interactive) | 576 | (interactive) |
| 548 | (let* ((pr (project-current t)) | 577 | (let* ((pr (project-current t)) |
| 549 | (dirs (project-roots pr))) | 578 | (dirs (list (project-root pr)))) |
| 550 | (project-find-file-in (thing-at-point 'filename) dirs pr))) | 579 | (project-find-file-in (thing-at-point 'filename) dirs pr))) |
| 551 | 580 | ||
| 552 | ;;;###autoload | 581 | ;;;###autoload |
| 553 | (defun project-or-external-find-file () | 582 | (defun project-or-external-find-file () |
| 554 | "Visit a file (with completion) in the current project's roots or external roots. | 583 | "Visit a file (with completion) in the current project or external roots. |
| 555 | The completion default is the filename at point, if one is | 584 | The completion default is the filename at point, if one is |
| 556 | recognized." | 585 | recognized." |
| 557 | (interactive) | 586 | (interactive) |
| 558 | (let* ((pr (project-current t)) | 587 | (let* ((pr (project-current t)) |
| 559 | (dirs (append | 588 | (dirs (cons |
| 560 | (project-roots pr) | 589 | (project-root pr) |
| 561 | (project-external-roots pr)))) | 590 | (project-external-roots pr)))) |
| 562 | (project-find-file-in (thing-at-point 'filename) dirs pr))) | 591 | (project-find-file-in (thing-at-point 'filename) dirs pr))) |
| 563 | 592 | ||
| @@ -660,5 +689,13 @@ loop using the command \\[fileloop-continue]." | |||
| 660 | from to (project-files (project-current t)) 'default) | 689 | from to (project-files (project-current t)) 'default) |
| 661 | (fileloop-continue)) | 690 | (fileloop-continue)) |
| 662 | 691 | ||
| 692 | ;;;###autoload | ||
| 693 | (defun project-compile () | ||
| 694 | "Run `compile' in the project root." | ||
| 695 | (interactive) | ||
| 696 | (let* ((pr (project-current t)) | ||
| 697 | (default-directory (project-root pr))) | ||
| 698 | (call-interactively 'compile))) | ||
| 699 | |||
| 663 | (provide 'project) | 700 | (provide 'project) |
| 664 | ;;; project.el ends here | 701 | ;;; project.el ends here |
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 67383b34154..1ca9f019638 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el | |||
| @@ -261,7 +261,6 @@ | |||
| 261 | (require 'ansi-color) | 261 | (require 'ansi-color) |
| 262 | (require 'cl-lib) | 262 | (require 'cl-lib) |
| 263 | (require 'comint) | 263 | (require 'comint) |
| 264 | (require 'json) | ||
| 265 | (require 'tramp-sh) | 264 | (require 'tramp-sh) |
| 266 | 265 | ||
| 267 | ;; Avoid compiler warnings | 266 | ;; Avoid compiler warnings |
| @@ -2276,6 +2275,18 @@ Do not set this variable directly, instead use | |||
| 2276 | Do not set this variable directly, instead use | 2275 | Do not set this variable directly, instead use |
| 2277 | `python-shell-prompt-set-calculated-regexps'.") | 2276 | `python-shell-prompt-set-calculated-regexps'.") |
| 2278 | 2277 | ||
| 2278 | (defalias 'python--parse-json-array | ||
| 2279 | (if (fboundp 'json-parse-string) | ||
| 2280 | (lambda (string) | ||
| 2281 | (json-parse-string string :array-type 'list)) | ||
| 2282 | (require 'json) | ||
| 2283 | (defvar json-array-type) | ||
| 2284 | (declare-function json-read-from-string "json" (string)) | ||
| 2285 | (lambda (string) | ||
| 2286 | (let ((json-array-type 'list)) | ||
| 2287 | (json-read-from-string string)))) | ||
| 2288 | "Parse the JSON array in STRING into a Lisp list.") | ||
| 2289 | |||
| 2279 | (defun python-shell-prompt-detect () | 2290 | (defun python-shell-prompt-detect () |
| 2280 | "Detect prompts for the current `python-shell-interpreter'. | 2291 | "Detect prompts for the current `python-shell-interpreter'. |
| 2281 | When prompts can be retrieved successfully from the | 2292 | When prompts can be retrieved successfully from the |
| @@ -2324,11 +2335,11 @@ detection and just returns nil." | |||
| 2324 | (catch 'prompts | 2335 | (catch 'prompts |
| 2325 | (dolist (line (split-string output "\n" t)) | 2336 | (dolist (line (split-string output "\n" t)) |
| 2326 | (let ((res | 2337 | (let ((res |
| 2327 | ;; Check if current line is a valid JSON array | 2338 | ;; Check if current line is a valid JSON array. |
| 2328 | (and (string= (substring line 0 2) "[\"") | 2339 | (and (string-prefix-p "[\"" line) |
| 2329 | (ignore-errors | 2340 | (ignore-errors |
| 2330 | ;; Return prompts as a list, not vector | 2341 | ;; Return prompts as a list. |
| 2331 | (append (json-read-from-string line) nil))))) | 2342 | (python--parse-json-array line))))) |
| 2332 | ;; The list must contain 3 strings, where the first | 2343 | ;; The list must contain 3 strings, where the first |
| 2333 | ;; is the input prompt, the second is the block | 2344 | ;; is the input prompt, the second is the block |
| 2334 | ;; prompt and the last one is the output prompt. The | 2345 | ;; prompt and the last one is the output prompt. The |
diff --git a/lisp/progmodes/which-func.el b/lisp/progmodes/which-func.el index 1cee552b0c0..266f40abbae 100644 --- a/lisp/progmodes/which-func.el +++ b/lisp/progmodes/which-func.el | |||
| @@ -186,7 +186,7 @@ and you want to simplify them for the mode line | |||
| 186 | "Non-nil means display current function name in mode line. | 186 | "Non-nil means display current function name in mode line. |
| 187 | This makes a difference only if `which-function-mode' is non-nil.") | 187 | This makes a difference only if `which-function-mode' is non-nil.") |
| 188 | 188 | ||
| 189 | (add-hook 'find-file-hook 'which-func-ff-hook t) | 189 | (add-hook 'after-change-major-mode-hook 'which-func-ff-hook t) |
| 190 | 190 | ||
| 191 | (defun which-func-try-to-enable () | 191 | (defun which-func-try-to-enable () |
| 192 | (unless (or (not which-function-mode) | 192 | (unless (or (not which-function-mode) |
| @@ -195,7 +195,7 @@ This makes a difference only if `which-function-mode' is non-nil.") | |||
| 195 | (member major-mode which-func-modes))))) | 195 | (member major-mode which-func-modes))))) |
| 196 | 196 | ||
| 197 | (defun which-func-ff-hook () | 197 | (defun which-func-ff-hook () |
| 198 | "File find hook for Which Function mode. | 198 | "`after-change-major-mode-hook' for Which Function mode. |
| 199 | It creates the Imenu index for the buffer, if necessary." | 199 | It creates the Imenu index for the buffer, if necessary." |
| 200 | (which-func-try-to-enable) | 200 | (which-func-try-to-enable) |
| 201 | 201 | ||
| @@ -282,52 +282,55 @@ If no function name is found, return nil." | |||
| 282 | (when (null name) | 282 | (when (null name) |
| 283 | (setq name (add-log-current-defun))) | 283 | (setq name (add-log-current-defun))) |
| 284 | ;; If Imenu is loaded, try to make an index alist with it. | 284 | ;; If Imenu is loaded, try to make an index alist with it. |
| 285 | ;; If `add-log-current-defun' ran and gave nil, accept that. | ||
| 285 | (when (and (null name) | 286 | (when (and (null name) |
| 286 | (boundp 'imenu--index-alist) | 287 | (null add-log-current-defun-function)) |
| 287 | (or (null imenu--index-alist) | 288 | (when (and (null name) |
| 288 | ;; Update if outdated | 289 | (boundp 'imenu--index-alist) |
| 289 | (/= (buffer-chars-modified-tick) imenu-menubar-modified-tick)) | 290 | (or (null imenu--index-alist) |
| 290 | (null which-function-imenu-failed)) | 291 | ;; Update if outdated |
| 291 | (ignore-errors (imenu--make-index-alist t)) | 292 | (/= (buffer-chars-modified-tick) imenu-menubar-modified-tick)) |
| 292 | (unless imenu--index-alist | 293 | (null which-function-imenu-failed)) |
| 293 | (set (make-local-variable 'which-function-imenu-failed) t))) | 294 | (ignore-errors (imenu--make-index-alist t)) |
| 294 | ;; If we have an index alist, use it. | 295 | (unless imenu--index-alist |
| 295 | (when (and (null name) | 296 | (set (make-local-variable 'which-function-imenu-failed) t))) |
| 296 | (boundp 'imenu--index-alist) imenu--index-alist) | 297 | ;; If we have an index alist, use it. |
| 297 | (let ((alist imenu--index-alist) | 298 | (when (and (null name) |
| 298 | (minoffset (point-max)) | 299 | (boundp 'imenu--index-alist) imenu--index-alist) |
| 299 | offset pair mark imstack namestack) | 300 | (let ((alist imenu--index-alist) |
| 300 | ;; Elements of alist are either ("name" . marker), or | 301 | (minoffset (point-max)) |
| 301 | ;; ("submenu" ("name" . marker) ... ). The list can be | 302 | offset pair mark imstack namestack) |
| 302 | ;; arbitrarily nested. | 303 | ;; Elements of alist are either ("name" . marker), or |
| 303 | (while (or alist imstack) | 304 | ;; ("submenu" ("name" . marker) ... ). The list can be |
| 304 | (if (null alist) | 305 | ;; arbitrarily nested. |
| 305 | (setq alist (car imstack) | 306 | (while (or alist imstack) |
| 306 | namestack (cdr namestack) | 307 | (if (null alist) |
| 307 | imstack (cdr imstack)) | 308 | (setq alist (car imstack) |
| 308 | 309 | namestack (cdr namestack) | |
| 309 | (setq pair (car-safe alist) | 310 | imstack (cdr imstack)) |
| 310 | alist (cdr-safe alist)) | 311 | |
| 311 | 312 | (setq pair (car-safe alist) | |
| 312 | (cond | 313 | alist (cdr-safe alist)) |
| 313 | ((atom pair)) ; Skip anything not a cons. | 314 | |
| 314 | 315 | (cond | |
| 315 | ((imenu--subalist-p pair) | 316 | ((atom pair)) ; Skip anything not a cons. |
| 316 | (setq imstack (cons alist imstack) | 317 | |
| 317 | namestack (cons (car pair) namestack) | 318 | ((imenu--subalist-p pair) |
| 318 | alist (cdr pair))) | 319 | (setq imstack (cons alist imstack) |
| 319 | 320 | namestack (cons (car pair) namestack) | |
| 320 | ((or (number-or-marker-p (setq mark (cdr pair))) | 321 | alist (cdr pair))) |
| 321 | (and (overlayp mark) | 322 | |
| 322 | (setq mark (overlay-start mark)))) | 323 | ((or (number-or-marker-p (setq mark (cdr pair))) |
| 323 | (when (and (>= (setq offset (- (point) mark)) 0) | 324 | (and (overlayp mark) |
| 324 | (< offset minoffset)) ; Find the closest item. | 325 | (setq mark (overlay-start mark)))) |
| 325 | (setq minoffset offset | 326 | (when (and (>= (setq offset (- (point) mark)) 0) |
| 326 | name (if (null which-func-imenu-joiner-function) | 327 | (< offset minoffset)) ; Find the closest item. |
| 327 | (car pair) | 328 | (setq minoffset offset |
| 328 | (funcall | 329 | name (if (null which-func-imenu-joiner-function) |
| 329 | which-func-imenu-joiner-function | 330 | (car pair) |
| 330 | (reverse (cons (car pair) namestack)))))))))))) | 331 | (funcall |
| 332 | which-func-imenu-joiner-function | ||
| 333 | (reverse (cons (car pair) namestack))))))))))))) | ||
| 331 | ;; Filter the name if requested. | 334 | ;; Filter the name if requested. |
| 332 | (when name | 335 | (when name |
| 333 | (if which-func-cleanup-function | 336 | (if which-func-cleanup-function |
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el index 7d1ee705b80..2477884f1ab 100644 --- a/lisp/progmodes/xref.el +++ b/lisp/progmodes/xref.el | |||
| @@ -268,8 +268,8 @@ find a search tool; by default, this uses \"find | grep\" in the | |||
| 268 | (lambda (dir) | 268 | (lambda (dir) |
| 269 | (xref-references-in-directory identifier dir)) | 269 | (xref-references-in-directory identifier dir)) |
| 270 | (let ((pr (project-current t))) | 270 | (let ((pr (project-current t))) |
| 271 | (append | 271 | (cons |
| 272 | (project-roots pr) | 272 | (project-root pr) |
| 273 | (project-external-roots pr))))) | 273 | (project-external-roots pr))))) |
| 274 | 274 | ||
| 275 | (cl-defgeneric xref-backend-apropos (backend pattern) | 275 | (cl-defgeneric xref-backend-apropos (backend pattern) |
diff --git a/lisp/subr.el b/lisp/subr.el index 971bce36b77..683e44123d7 100644 --- a/lisp/subr.el +++ b/lisp/subr.el | |||
| @@ -4117,7 +4117,11 @@ MODES is as for `set-default-file-modes'." | |||
| 4117 | ;; now, but it generates slower code. | 4117 | ;; now, but it generates slower code. |
| 4118 | (defmacro save-match-data (&rest body) | 4118 | (defmacro save-match-data (&rest body) |
| 4119 | "Execute the BODY forms, restoring the global value of the match data. | 4119 | "Execute the BODY forms, restoring the global value of the match data. |
| 4120 | The value returned is the value of the last form in BODY." | 4120 | The value returned is the value of the last form in BODY. |
| 4121 | NOTE: The convention in Elisp is that any function, except for a few | ||
| 4122 | exceptions like car/assoc/+/goto-char, can clobber the match data, | ||
| 4123 | so `save-match-data' should normally be used to save *your* match data | ||
| 4124 | rather than your caller's match data." | ||
| 4121 | ;; It is better not to use backquote here, | 4125 | ;; It is better not to use backquote here, |
| 4122 | ;; because that makes a bootstrapping problem | 4126 | ;; because that makes a bootstrapping problem |
| 4123 | ;; if you need to recompile all the Lisp files using interpreted code. | 4127 | ;; if you need to recompile all the Lisp files using interpreted code. |
diff --git a/lisp/vc/vc-dir.el b/lisp/vc/vc-dir.el index 0c9e656add4..a86c37c24ae 100644 --- a/lisp/vc/vc-dir.el +++ b/lisp/vc/vc-dir.el | |||
| @@ -1106,6 +1106,7 @@ the *vc-dir* buffer. | |||
| 1106 | (set (make-local-variable 'vc-dir-backend) use-vc-backend) | 1106 | (set (make-local-variable 'vc-dir-backend) use-vc-backend) |
| 1107 | (set (make-local-variable 'desktop-save-buffer) | 1107 | (set (make-local-variable 'desktop-save-buffer) |
| 1108 | 'vc-dir-desktop-buffer-misc-data) | 1108 | 'vc-dir-desktop-buffer-misc-data) |
| 1109 | (setq-local bookmark-make-record-function #'vc-dir-bookmark-make-record) | ||
| 1109 | (setq buffer-read-only t) | 1110 | (setq buffer-read-only t) |
| 1110 | (when (boundp 'tool-bar-map) | 1111 | (when (boundp 'tool-bar-map) |
| 1111 | (set (make-local-variable 'tool-bar-map) vc-dir-tool-bar-map)) | 1112 | (set (make-local-variable 'tool-bar-map) vc-dir-tool-bar-map)) |
| @@ -1466,6 +1467,41 @@ These are the commands available for use in the file status buffer: | |||
| 1466 | '(vc-dir-mode . vc-dir-restore-desktop-buffer)) | 1467 | '(vc-dir-mode . vc-dir-restore-desktop-buffer)) |
| 1467 | 1468 | ||
| 1468 | 1469 | ||
| 1470 | ;;; Support for bookmark.el (adapted from what info.el does). | ||
| 1471 | |||
| 1472 | (declare-function bookmark-make-record-default | ||
| 1473 | "bookmark" (&optional no-file no-context posn)) | ||
| 1474 | (declare-function bookmark-prop-get "bookmark" (bookmark prop)) | ||
| 1475 | (declare-function bookmark-default-handler "bookmark" (bmk)) | ||
| 1476 | (declare-function bookmark-get-bookmark-record "bookmark" (bmk)) | ||
| 1477 | |||
| 1478 | (defun vc-dir-bookmark-make-record () | ||
| 1479 | "Make record used to bookmark a `vc-dir' buffer. | ||
| 1480 | This implements the `bookmark-make-record-function' type for | ||
| 1481 | `vc-dir' buffers." | ||
| 1482 | (let* ((bookmark-name | ||
| 1483 | (concat "(" (symbol-name vc-dir-backend) ") " | ||
| 1484 | (file-name-nondirectory | ||
| 1485 | (directory-file-name default-directory)))) | ||
| 1486 | (defaults (list bookmark-name default-directory))) | ||
| 1487 | `(,bookmark-name | ||
| 1488 | ,@(bookmark-make-record-default 'no-file) | ||
| 1489 | (filename . ,default-directory) | ||
| 1490 | (handler . vc-dir-bookmark-jump) | ||
| 1491 | (defaults . ,defaults)))) | ||
| 1492 | |||
| 1493 | ;;;###autoload | ||
| 1494 | (defun vc-dir-bookmark-jump (bmk) | ||
| 1495 | "Provides the bookmark-jump behavior for a `vc-dir' buffer. | ||
| 1496 | This implements the `handler' function interface for the record | ||
| 1497 | type returned by `vc-dir-bookmark-make-record'." | ||
| 1498 | (let* ((file (bookmark-prop-get bmk 'filename)) | ||
| 1499 | (buf (save-window-excursion | ||
| 1500 | (vc-dir file) (current-buffer)))) | ||
| 1501 | (bookmark-default-handler | ||
| 1502 | `("" (buffer . ,buf) . ,(bookmark-get-bookmark-record bmk))))) | ||
| 1503 | |||
| 1504 | |||
| 1469 | (provide 'vc-dir) | 1505 | (provide 'vc-dir) |
| 1470 | 1506 | ||
| 1471 | ;;; vc-dir.el ends here | 1507 | ;;; vc-dir.el ends here |
diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 2caa287bce2..dcb52282656 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el | |||
| @@ -72,6 +72,7 @@ | |||
| 72 | ;; by git, so it's probably | 72 | ;; by git, so it's probably |
| 73 | ;; not a good idea. | 73 | ;; not a good idea. |
| 74 | ;; - merge-news (file) see `merge-file' | 74 | ;; - merge-news (file) see `merge-file' |
| 75 | ;; - mark-resolved (file) OK | ||
| 75 | ;; - steal-lock (file &optional revision) NOT NEEDED | 76 | ;; - steal-lock (file &optional revision) NOT NEEDED |
| 76 | ;; HISTORY FUNCTIONS | 77 | ;; HISTORY FUNCTIONS |
| 77 | ;; * print-log (files buffer &optional shortlog start-revision limit) OK | 78 | ;; * print-log (files buffer &optional shortlog start-revision limit) OK |
| @@ -1530,6 +1531,9 @@ This requires git 1.8.4 or later, for the \"-L\" option of \"git log\"." | |||
| 1530 | (defun vc-git-rename-file (old new) | 1531 | (defun vc-git-rename-file (old new) |
| 1531 | (vc-git-command nil 0 (list old new) "mv" "-f" "--")) | 1532 | (vc-git-command nil 0 (list old new) "mv" "-f" "--")) |
| 1532 | 1533 | ||
| 1534 | (defun vc-git-mark-resolved (files) | ||
| 1535 | (vc-git-command nil 0 files "add")) | ||
| 1536 | |||
| 1533 | (defvar vc-git-extra-menu-map | 1537 | (defvar vc-git-extra-menu-map |
| 1534 | (let ((map (make-sparse-keymap))) | 1538 | (let ((map (make-sparse-keymap))) |
| 1535 | (define-key map [git-grep] | 1539 | (define-key map [git-grep] |
diff --git a/lisp/vc/vc-hooks.el b/lisp/vc/vc-hooks.el index 2ca9d3e620c..ce72a49b955 100644 --- a/lisp/vc/vc-hooks.el +++ b/lisp/vc/vc-hooks.el | |||
| @@ -498,7 +498,7 @@ status of this file. Otherwise, the value returned is one of: | |||
| 498 | "Return the repository version from which FILE was checked out. | 498 | "Return the repository version from which FILE was checked out. |
| 499 | If FILE is not registered, this function always returns nil." | 499 | If FILE is not registered, this function always returns nil." |
| 500 | (or (vc-file-getprop file 'vc-working-revision) | 500 | (or (vc-file-getprop file 'vc-working-revision) |
| 501 | (progn | 501 | (let ((default-directory (file-name-directory file))) |
| 502 | (setq backend (or backend (vc-backend file))) | 502 | (setq backend (or backend (vc-backend file))) |
| 503 | (when backend | 503 | (when backend |
| 504 | (vc-file-setprop file 'vc-working-revision | 504 | (vc-file-setprop file 'vc-working-revision |
diff --git a/lisp/version.el b/lisp/version.el index 24da21c731c..b247232dcfd 100644 --- a/lisp/version.el +++ b/lisp/version.el | |||
| @@ -163,8 +163,4 @@ correspond to the running Emacs. | |||
| 163 | Optional argument DIR is a directory to use instead of `source-directory'." | 163 | Optional argument DIR is a directory to use instead of `source-directory'." |
| 164 | (emacs-repository-branch-git (or dir source-directory))) | 164 | (emacs-repository-branch-git (or dir source-directory))) |
| 165 | 165 | ||
| 166 | ;; We put version info into the executable in the form that `ident' uses. | ||
| 167 | (purecopy (concat "\n$Id: " (subst-char-in-string ?\n ?\s (emacs-version)) | ||
| 168 | " $\n")) | ||
| 169 | |||
| 170 | ;;; version.el ends here | 166 | ;;; version.el ends here |
diff --git a/lisp/xml.el b/lisp/xml.el index dc774a202cf..767cf042846 100644 --- a/lisp/xml.el +++ b/lisp/xml.el | |||
| @@ -1023,9 +1023,17 @@ entity references (e.g., replace each & with &). | |||
| 1023 | XML character data must not contain & or < characters, nor the > | 1023 | XML character data must not contain & or < characters, nor the > |
| 1024 | character under some circumstances. The XML spec does not impose | 1024 | character under some circumstances. The XML spec does not impose |
| 1025 | restriction on \" or \\=', but we just substitute for these too | 1025 | restriction on \" or \\=', but we just substitute for these too |
| 1026 | \(as is permitted by the spec)." | 1026 | \(as is permitted by the spec). |
| 1027 | |||
| 1028 | If STRING contains characters that are invalid in XML (as defined | ||
| 1029 | by https://www.w3.org/TR/xml/#charsets), signal an error of type | ||
| 1030 | `xml-invalid-character'." | ||
| 1027 | (with-temp-buffer | 1031 | (with-temp-buffer |
| 1028 | (insert string) | 1032 | (insert string) |
| 1033 | (goto-char (point-min)) | ||
| 1034 | (when (re-search-forward | ||
| 1035 | "[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD\U00010000-\U0010FFFF]") | ||
| 1036 | (signal 'xml-invalid-character (list (char-before) (match-beginning 0)))) | ||
| 1029 | (dolist (substitution '(("&" . "&") | 1037 | (dolist (substitution '(("&" . "&") |
| 1030 | ("<" . "<") | 1038 | ("<" . "<") |
| 1031 | (">" . ">") | 1039 | (">" . ">") |
| @@ -1036,6 +1044,9 @@ restriction on \" or \\=', but we just substitute for these too | |||
| 1036 | (replace-match (cdr substitution) t t nil))) | 1044 | (replace-match (cdr substitution) t t nil))) |
| 1037 | (buffer-string))) | 1045 | (buffer-string))) |
| 1038 | 1046 | ||
| 1047 | (define-error 'xml-invalid-character "Invalid XML character" | ||
| 1048 | 'wrong-type-argument) | ||
| 1049 | |||
| 1039 | (defun xml-debug-print-internal (xml indent-string) | 1050 | (defun xml-debug-print-internal (xml indent-string) |
| 1040 | "Outputs the XML tree in the current buffer. | 1051 | "Outputs the XML tree in the current buffer. |
| 1041 | The first line is indented with INDENT-STRING." | 1052 | The first line is indented with INDENT-STRING." |