aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorAndrea Corallo2020-08-09 15:03:23 +0200
committerAndrea Corallo2020-08-09 15:03:23 +0200
commit12a982d9789052d8e85efcacb4b311f4876c882a (patch)
treea452a8e888c6ee9c85d6a487359b7a1c0c9fa15b /lisp
parent80d7f710f2fab902e46aa3fddb8e1c1795420af3 (diff)
parent8e82baf5a730ff542118ddba5b76afdc1db643f6 (diff)
downloademacs-12a982d9789052d8e85efcacb4b311f4876c882a.tar.gz
emacs-12a982d9789052d8e85efcacb4b311f4876c882a.zip
Merge remote-tracking branch 'savannah/master' into HEAD
Diffstat (limited to 'lisp')
-rw-r--r--lisp/Makefile.in1
-rw-r--r--lisp/arc-mode.el115
-rw-r--r--lisp/bookmark.el13
-rw-r--r--lisp/buff-menu.el60
-rw-r--r--lisp/button.el51
-rw-r--r--lisp/calendar/cal-dst.el16
-rw-r--r--lisp/calendar/calendar.el7
-rw-r--r--lisp/calendar/solar.el10
-rw-r--r--lisp/calendar/time-date.el15
-rw-r--r--lisp/cus-dep.el10
-rw-r--r--lisp/cus-edit.el5
-rw-r--r--lisp/custom.el14
-rw-r--r--lisp/dired-aux.el20
-rw-r--r--lisp/dired.el2
-rw-r--r--lisp/emacs-lisp/autoload.el16
-rw-r--r--lisp/emacs-lisp/byte-opt.el29
-rw-r--r--lisp/emacs-lisp/byte-run.el17
-rw-r--r--lisp/emacs-lisp/bytecomp.el93
-rw-r--r--lisp/emacs-lisp/cl-generic.el16
-rw-r--r--lisp/emacs-lisp/cl-macs.el7
-rw-r--r--lisp/emacs-lisp/edebug.el31
-rw-r--r--lisp/emacs-lisp/hierarchy.el579
-rw-r--r--lisp/emacs-lisp/seq.el1
-rw-r--r--lisp/epa-file.el30
-rw-r--r--lisp/erc/erc-capab.el16
-rw-r--r--lisp/erc/erc-compat.el10
-rw-r--r--lisp/erc/erc-dcc.el10
-rw-r--r--lisp/erc/erc-list.el28
-rw-r--r--lisp/erc/erc-log.el2
-rw-r--r--lisp/erc/erc-match.el6
-rw-r--r--lisp/erc/erc-networks.el2
-rw-r--r--lisp/erc/erc.el132
-rw-r--r--lisp/files.el2
-rw-r--r--lisp/finder.el2
-rw-r--r--lisp/generic-x.el2
-rw-r--r--lisp/gnus/gnus-art.el34
-rw-r--r--lisp/gnus/gnus-icalendar.el3
-rw-r--r--lisp/gnus/gnus-sum.el2
-rw-r--r--lisp/gnus/gnus-util.el1
-rw-r--r--lisp/gnus/gnus-win.el2
-rw-r--r--lisp/gnus/message.el201
-rw-r--r--lisp/gnus/mm-decode.el6
-rw-r--r--lisp/gnus/mm-view.el10
-rw-r--r--lisp/gnus/mml-sec.el12
-rw-r--r--lisp/gnus/mml.el13
-rw-r--r--lisp/gnus/smime.el3
-rw-r--r--lisp/help-fns.el44
-rw-r--r--lisp/hi-lock.el4
-rw-r--r--lisp/ibuf-ext.el6
-rw-r--r--lisp/image-file.el12
-rw-r--r--lisp/image-mode.el153
-rw-r--r--lisp/image/image-converter.el14
-rw-r--r--lisp/international/ja-dic-cnv.el13
-rw-r--r--lisp/mouse.el5
-rw-r--r--lisp/net/browse-url.el136
-rw-r--r--lisp/net/eww.el18
-rw-r--r--lisp/net/tramp-adb.el323
-rw-r--r--lisp/net/tramp-sh.el462
-rw-r--r--lisp/net/tramp.el169
-rw-r--r--lisp/outline.el11
-rw-r--r--lisp/play/snake.el1
-rw-r--r--lisp/progmodes/cperl-mode.el45
-rw-r--r--lisp/progmodes/project.el8
-rw-r--r--lisp/progmodes/sh-script.el2
-rw-r--r--lisp/progmodes/sql.el20
-rw-r--r--lisp/recentf.el3
-rw-r--r--lisp/saveplace.el12
-rw-r--r--lisp/scroll-lock.el2
-rw-r--r--lisp/simple.el37
-rw-r--r--lisp/skeleton.el101
-rw-r--r--lisp/so-long.el88
-rw-r--r--lisp/subr.el6
-rw-r--r--lisp/tar-mode.el50
-rw-r--r--lisp/textmodes/css-mode.el2
-rw-r--r--lisp/textmodes/sgml-mode.el10
-rw-r--r--lisp/thingatpt.el2
-rw-r--r--lisp/vc/vc-hg.el61
-rw-r--r--lisp/wdired.el15
-rw-r--r--lisp/whitespace.el25
-rw-r--r--lisp/wid-edit.el57
-rw-r--r--lisp/x-dnd.el4
81 files changed, 2566 insertions, 1012 deletions
diff --git a/lisp/Makefile.in b/lisp/Makefile.in
index 035720b49b7..9bcceceb0ee 100644
--- a/lisp/Makefile.in
+++ b/lisp/Makefile.in
@@ -205,7 +205,6 @@ $(lisp)/finder-inf.el:
205 205
206autoloads .PHONY: $(lisp)/loaddefs.el 206autoloads .PHONY: $(lisp)/loaddefs.el
207$(lisp)/loaddefs.el: gen-lisp $(LOADDEFS) 207$(lisp)/loaddefs.el: gen-lisp $(LOADDEFS)
208 @echo Directories for loaddefs: ${SUBDIRS_ALMOST}
209 $(AM_V_GEN)$(emacs) -l autoload \ 208 $(AM_V_GEN)$(emacs) -l autoload \
210 --eval '(setq autoload-ensure-writable t)' \ 209 --eval '(setq autoload-ensure-writable t)' \
211 --eval '(setq autoload-builtin-package-versions t)' \ 210 --eval '(setq autoload-builtin-package-versions t)' \
diff --git a/lisp/arc-mode.el b/lisp/arc-mode.el
index 6781c292d82..ae85fc55add 100644
--- a/lisp/arc-mode.el
+++ b/lisp/arc-mode.el
@@ -391,6 +391,7 @@ file. Archive and member name will be added."
391 (define-key map "e" 'archive-extract) 391 (define-key map "e" 'archive-extract)
392 (define-key map "f" 'archive-extract) 392 (define-key map "f" 'archive-extract)
393 (define-key map "\C-m" 'archive-extract) 393 (define-key map "\C-m" 'archive-extract)
394 (define-key map "C" 'archive-copy-file)
394 (define-key map "m" 'archive-mark) 395 (define-key map "m" 'archive-mark)
395 (define-key map "n" 'archive-next-line) 396 (define-key map "n" 'archive-next-line)
396 (define-key map "\C-n" 'archive-next-line) 397 (define-key map "\C-n" 'archive-next-line)
@@ -430,6 +431,9 @@ file. Archive and member name will be added."
430 (define-key map [menu-bar immediate view] 431 (define-key map [menu-bar immediate view]
431 '(menu-item "View This File" archive-view 432 '(menu-item "View This File" archive-view
432 :help "Display file at cursor in View Mode")) 433 :help "Display file at cursor in View Mode"))
434 (define-key map [menu-bar immediate view]
435 '(menu-item "Copy This File" archive-copy-file
436 :help "Copy file at cursor to another location"))
433 (define-key map [menu-bar immediate display] 437 (define-key map [menu-bar immediate display]
434 '(menu-item "Display in Other Window" archive-display-other-window 438 '(menu-item "Display in Other Window" archive-display-other-window
435 :help "Display file at cursor in another window")) 439 :help "Display file at cursor in another window"))
@@ -989,6 +993,75 @@ using `make-temp-file', and the generated name is returned."
989 (kill-local-variable 'buffer-file-coding-system) 993 (kill-local-variable 'buffer-file-coding-system)
990 (after-insert-file-set-coding (- (point-max) (point-min)))))) 994 (after-insert-file-set-coding (- (point-max) (point-min))))))
991 995
996(defun archive-goto-file (file)
997 "Go to FILE in the current buffer.
998FILE should be a relative file name. If FILE can't be found,
999return nil. Otherwise point is returned."
1000 (let ((start (point))
1001 found)
1002 (goto-char (point-min))
1003 (while (and (not found)
1004 (not (eobp)))
1005 (forward-line 1)
1006 (when-let ((descr (archive-get-descr t)))
1007 (when (equal (archive--file-desc-ext-file-name descr) file)
1008 (setq found t))))
1009 (if (not found)
1010 (progn
1011 (goto-char start)
1012 nil)
1013 (point))))
1014
1015(defun archive-next-file-displayer (file regexp n)
1016 "Return a closure to display the next file after FILE that matches REGEXP."
1017 (let ((short (replace-regexp-in-string "\\`.*:" "" file))
1018 next)
1019 (archive-goto-file short)
1020 (while (and (not next)
1021 ;; Stop if we reach the end/start of the buffer.
1022 (if (> n 0)
1023 (not (eobp))
1024 (not (save-excursion
1025 (beginning-of-line)
1026 (bobp)))))
1027 (archive-next-line n)
1028 (when-let ((descr (archive-get-descr t)))
1029 (let ((candidate (archive--file-desc-ext-file-name descr))
1030 (buffer (current-buffer)))
1031 (when (and candidate
1032 (string-match-p regexp candidate))
1033 (setq next (lambda ()
1034 (kill-buffer (current-buffer))
1035 (switch-to-buffer buffer)
1036 (archive-extract)))))))
1037 (unless next
1038 ;; If we didn't find a next/prev file, then restore
1039 ;; point.
1040 (archive-goto-file short))
1041 next))
1042
1043(defun archive-copy-file (file new-name)
1044 "Copy FILE to a location specified by NEW-NAME.
1045Interactively, FILE is the file at point, and the function prompts
1046for NEW-NAME."
1047 (interactive
1048 (let ((name (archive--file-desc-ext-file-name (archive-get-descr))))
1049 (list name
1050 (read-file-name (format "Copy %s to: " name)))))
1051 (when (file-directory-p new-name)
1052 (setq new-name (expand-file-name file new-name)))
1053 (when (and (file-exists-p new-name)
1054 (not (yes-or-no-p (format "%s already exists; overwrite? "
1055 new-name))))
1056 (user-error "Not overwriting %s" new-name))
1057 (let* ((descr (archive-get-descr))
1058 (archive (buffer-file-name))
1059 (extractor (archive-name "extract"))
1060 (ename (archive--file-desc-ext-file-name descr)))
1061 (with-temp-buffer
1062 (archive--extract-file extractor archive ename)
1063 (write-region (point-min) (point-max) new-name))))
1064
992(defun archive-extract (&optional other-window-p event) 1065(defun archive-extract (&optional other-window-p event)
993 "In archive mode, extract this entry of the archive into its own buffer." 1066 "In archive mode, extract this entry of the archive into its own buffer."
994 (interactive (list nil last-input-event)) 1067 (interactive (list nil last-input-event))
@@ -1030,26 +1103,7 @@ using `make-temp-file', and the generated name is returned."
1030 (setq archive-subfile-mode descr) 1103 (setq archive-subfile-mode descr)
1031 (setq archive-file-name-coding-system file-name-coding) 1104 (setq archive-file-name-coding-system file-name-coding)
1032 (if (and 1105 (if (and
1033 (null 1106 (null (archive--extract-file extractor archive ename))
1034 (let (;; We may have to encode the file name argument for
1035 ;; external programs.
1036 (coding-system-for-write
1037 (and enable-multibyte-characters
1038 archive-file-name-coding-system))
1039 ;; We read an archive member by no-conversion at
1040 ;; first, then decode appropriately by calling
1041 ;; archive-set-buffer-as-visiting-file later.
1042 (coding-system-for-read 'no-conversion)
1043 ;; Avoid changing dir mtime by lock_file
1044 (create-lockfiles nil))
1045 (condition-case err
1046 (if (fboundp extractor)
1047 (funcall extractor archive ename)
1048 (archive-*-extract archive ename
1049 (symbol-value extractor)))
1050 (error
1051 (ding (message "%s" (error-message-string err)))
1052 nil))))
1053 just-created) 1107 just-created)
1054 (progn 1108 (progn
1055 (set-buffer-modified-p nil) 1109 (set-buffer-modified-p nil)
@@ -1082,6 +1136,27 @@ using `make-temp-file', and the generated name is returned."
1082 (other-window-p (switch-to-buffer-other-window buffer)) 1136 (other-window-p (switch-to-buffer-other-window buffer))
1083 (t (switch-to-buffer buffer)))))) 1137 (t (switch-to-buffer buffer))))))
1084 1138
1139(defun archive--extract-file (extractor archive ename)
1140 (let (;; We may have to encode the file name argument for
1141 ;; external programs.
1142 (coding-system-for-write
1143 (and enable-multibyte-characters
1144 archive-file-name-coding-system))
1145 ;; We read an archive member by no-conversion at
1146 ;; first, then decode appropriately by calling
1147 ;; archive-set-buffer-as-visiting-file later.
1148 (coding-system-for-read 'no-conversion)
1149 ;; Avoid changing dir mtime by lock_file
1150 (create-lockfiles nil))
1151 (condition-case err
1152 (if (fboundp extractor)
1153 (funcall extractor archive ename)
1154 (archive-*-extract archive ename
1155 (symbol-value extractor)))
1156 (error
1157 (ding (message "%s" (error-message-string err)))
1158 nil))))
1159
1085(defun archive-*-extract (archive name command) 1160(defun archive-*-extract (archive name command)
1086 (let* ((default-directory (file-name-as-directory archive-tmpdir)) 1161 (let* ((default-directory (file-name-as-directory archive-tmpdir))
1087 (tmpfile (expand-file-name (file-name-nondirectory name) 1162 (tmpfile (expand-file-name (file-name-nondirectory name)
diff --git a/lisp/bookmark.el b/lisp/bookmark.el
index de7d60f97eb..fb293adb779 100644
--- a/lisp/bookmark.el
+++ b/lisp/bookmark.el
@@ -1667,6 +1667,19 @@ Don't affect the buffer ring order."
1667 1667
1668 1668
1669;;;###autoload 1669;;;###autoload
1670(defun bookmark-bmenu-get-buffer ()
1671 "Return the Bookmark List, building it if it doesn't exists.
1672Don't affect the buffer ring order."
1673 (or (get-buffer bookmark-bmenu-buffer)
1674 (save-excursion
1675 (save-window-excursion
1676 (bookmark-bmenu-list)
1677 (get-buffer bookmark-bmenu-buffer)))))
1678
1679(custom-add-choice 'tab-bar-new-tab-choice
1680 '(const :tag "Bookmark List" bookmark-bmenu-get-buffer))
1681
1682;;;###autoload
1670(defun bookmark-bmenu-list () 1683(defun bookmark-bmenu-list ()
1671 "Display a list of existing bookmarks. 1684 "Display a list of existing bookmarks.
1672The list is displayed in a buffer named `*Bookmark List*'. 1685The list is displayed in a buffer named `*Bookmark List*'.
diff --git a/lisp/buff-menu.el b/lisp/buff-menu.el
index 655a76a713c..aa5c47ca7f4 100644
--- a/lisp/buff-menu.el
+++ b/lisp/buff-menu.el
@@ -69,11 +69,26 @@ minus `Buffer-menu-size-width'. This use is deprecated."
69 "use `Buffer-menu-name-width' and `Buffer-menu-size-width' instead." 69 "use `Buffer-menu-name-width' and `Buffer-menu-size-width' instead."
70 "24.3") 70 "24.3")
71 71
72(defcustom Buffer-menu-name-width 19 72(defun Buffer-menu--dynamic-name-width (buffers)
73 "Width of buffer name column in the Buffer Menu." 73 "Return a name column width based on the current window width.
74 :type 'number 74The width will never exceed the actual width of the buffer names,
75but will never be narrower than 19 characters."
76 (max 19
77 ;; This gives 19 on an 80 column window, and take up
78 ;; proportionally more space as the window widens.
79 (min (truncate (/ (window-width) 4.2))
80 (apply #'max 0 (mapcar (lambda (b)
81 (length (buffer-name b)))
82 buffers)))))
83
84(defcustom Buffer-menu-name-width #'Buffer-menu--dynamic-name-width
85 "Width of buffer name column in the Buffer Menu.
86This can either be a number (used directly) or a function that
87will be called with the list of buffers and should return a
88number."
89 :type '(choice function number)
75 :group 'Buffer-menu 90 :group 'Buffer-menu
76 :version "24.3") 91 :version "28.1")
77 92
78(defcustom Buffer-menu-size-width 7 93(defcustom Buffer-menu-size-width 7
79 "Width of buffer size column in the Buffer Menu." 94 "Width of buffer size column in the Buffer Menu."
@@ -488,8 +503,9 @@ Buffers marked with \\<Buffer-menu-mode-map>`\\[Buffer-menu-delete]' are deleted
488(defun Buffer-menu-select () 503(defun Buffer-menu-select ()
489 "Select this line's buffer; also, display buffers marked with `>'. 504 "Select this line's buffer; also, display buffers marked with `>'.
490You can mark buffers with the \\<Buffer-menu-mode-map>`\\[Buffer-menu-mark]' command. 505You can mark buffers with the \\<Buffer-menu-mode-map>`\\[Buffer-menu-mark]' command.
506
491This command deletes and replaces all the previously existing windows 507This command deletes and replaces all the previously existing windows
492in the selected frame." 508in the selected frame, and will remove any marks."
493 (interactive) 509 (interactive)
494 (let* ((this-buffer (Buffer-menu-buffer t)) 510 (let* ((this-buffer (Buffer-menu-buffer t))
495 (menu-buffer (current-buffer)) 511 (menu-buffer (current-buffer))
@@ -645,25 +661,11 @@ means list those buffers and no others."
645 661
646(defun list-buffers--refresh (&optional buffer-list old-buffer) 662(defun list-buffers--refresh (&optional buffer-list old-buffer)
647 ;; Set up `tabulated-list-format'. 663 ;; Set up `tabulated-list-format'.
648 (let ((name-width Buffer-menu-name-width) 664 (let ((size-width Buffer-menu-size-width)
649 (size-width Buffer-menu-size-width)
650 (marked-buffers (Buffer-menu-marked-buffers)) 665 (marked-buffers (Buffer-menu-marked-buffers))
651 (buffer-menu-buffer (current-buffer)) 666 (buffer-menu-buffer (current-buffer))
652 (show-non-file (not Buffer-menu-files-only)) 667 (show-non-file (not Buffer-menu-files-only))
653 entries) 668 entries name-width)
654 ;; Handle obsolete variable:
655 (if Buffer-menu-buffer+size-width
656 (setq name-width (- Buffer-menu-buffer+size-width size-width)))
657 (setq tabulated-list-format
658 (vector '("C" 1 t :pad-right 0)
659 '("R" 1 t :pad-right 0)
660 '("M" 1 t)
661 `("Buffer" ,name-width t)
662 `("Size" ,size-width tabulated-list-entry-size->
663 :right-align t)
664 `("Mode" ,Buffer-menu-mode-width t)
665 '("File" 1 t)))
666 (setq tabulated-list-use-header-line Buffer-menu-use-header-line)
667 ;; Collect info for each buffer we're interested in. 669 ;; Collect info for each buffer we're interested in.
668 (dolist (buffer (or buffer-list 670 (dolist (buffer (or buffer-list
669 (buffer-list (if Buffer-menu-use-frame-buffer-list 671 (buffer-list (if Buffer-menu-use-frame-buffer-list
@@ -693,6 +695,22 @@ means list those buffers and no others."
693 nil nil buffer))) 695 nil nil buffer)))
694 (Buffer-menu--pretty-file-name file))) 696 (Buffer-menu--pretty-file-name file)))
695 entries))))) 697 entries)))))
698 (setq name-width (if (functionp Buffer-menu-name-width)
699 (funcall Buffer-menu-name-width (mapcar #'car entries))
700 Buffer-menu-name-width))
701 ;; Handle obsolete variable:
702 (if Buffer-menu-buffer+size-width
703 (setq name-width (- Buffer-menu-buffer+size-width size-width)))
704 (setq tabulated-list-format
705 (vector '("C" 1 t :pad-right 0)
706 '("R" 1 t :pad-right 0)
707 '("M" 1 t)
708 `("Buffer" ,name-width t)
709 `("Size" ,size-width tabulated-list-entry-size->
710 :right-align t)
711 `("Mode" ,Buffer-menu-mode-width t)
712 '("File" 1 t)))
713 (setq tabulated-list-use-header-line Buffer-menu-use-header-line)
696 (setq tabulated-list-entries (nreverse entries))) 714 (setq tabulated-list-entries (nreverse entries)))
697 (tabulated-list-init-header)) 715 (tabulated-list-init-header))
698 716
diff --git a/lisp/button.el b/lisp/button.el
index d9c36a0375c..03ab59b109c 100644
--- a/lisp/button.el
+++ b/lisp/button.el
@@ -464,8 +464,12 @@ see).
464POS defaults to point, except when `push-button' is invoked 464POS defaults to point, except when `push-button' is invoked
465interactively as the result of a mouse-event, in which case, the 465interactively as the result of a mouse-event, in which case, the
466mouse event is used. 466mouse event is used.
467
467If there's no button at POS, do nothing and return nil, otherwise 468If there's no button at POS, do nothing and return nil, otherwise
468return t." 469return t.
470
471To get a description of what function will called when pushing a
472butting, use the `button-describe' command."
469 (interactive 473 (interactive
470 (list (if (integerp last-command-event) (point) last-command-event))) 474 (list (if (integerp last-command-event) (point) last-command-event)))
471 (if (and (not (integerp pos)) (eventp pos)) 475 (if (and (not (integerp pos)) (eventp pos))
@@ -555,6 +559,51 @@ Returns the button found."
555 (interactive "p\nd\nd") 559 (interactive "p\nd\nd")
556 (forward-button (- n) wrap display-message no-error)) 560 (forward-button (- n) wrap display-message no-error))
557 561
562(defun button--describe (properties)
563 "Describe a button's PROPERTIES (an alist) in a *Help* buffer.
564This is a helper function for `button-describe', in order to be possible to
565use `help-setup-xref'.
566
567Each element of PROPERTIES should be of the form (PROPERTY . VALUE)."
568 (help-setup-xref (list #'button--describe properties)
569 (called-interactively-p 'interactive))
570 (with-help-window (help-buffer)
571 (with-current-buffer (help-buffer)
572 (insert (format-message "This button's type is `%s'."
573 (alist-get 'type properties)))
574 (dolist (prop '(action mouse-action))
575 (let ((name (symbol-name prop))
576 (val (alist-get prop properties)))
577 (when (functionp val)
578 (insert "\n\n"
579 (propertize (capitalize name) 'face 'bold)
580 "\nThe " name " of this button is")
581 (if (symbolp val)
582 (progn
583 (insert (format-message " `%s',\nwhich is " val))
584 (describe-function-1 val))
585 (insert "\n")
586 (princ val))))))))
587
588(defun button-describe (&optional button-or-pos)
589 "Display a buffer with information about the button at point.
590
591When called from Lisp, pass BUTTON-OR-POS as the button to describe, or a
592buffer position where a button is present. If BUTTON-OR-POS is nil, the
593button at point is the button to describe."
594 (interactive "d")
595 (let* ((button (cond ((integer-or-marker-p button-or-pos)
596 (button-at button-or-pos))
597 ((null button-or-pos) (button-at (point)))
598 ((overlayp button-or-pos) button-or-pos)))
599 (props (and button
600 (mapcar (lambda (prop)
601 (cons prop (button-get button prop)))
602 '(type action mouse-action)))))
603 (when props
604 (button--describe props)
605 t)))
606
558(provide 'button) 607(provide 'button)
559 608
560;;; button.el ends here 609;;; button.el ends here
diff --git a/lisp/calendar/cal-dst.el b/lisp/calendar/cal-dst.el
index 3db12e668ab..af6acaf09ad 100644
--- a/lisp/calendar/cal-dst.el
+++ b/lisp/calendar/cal-dst.el
@@ -350,17 +350,29 @@ If the locale never uses daylight saving time, set this to 0."
350 :group 'calendar-dst) 350 :group 'calendar-dst)
351 351
352(defcustom calendar-standard-time-zone-name 352(defcustom calendar-standard-time-zone-name
353 (or (nth 2 calendar-current-time-zone-cache) "EST") 353 (if calendar-use-numeric-time-zones
354 (if calendar-current-time-zone-cache
355 (format-time-string
356 "%z" 0 (* 60 (car calendar-current-time-zone-cache)))
357 "+0000")
358 (or (nth 2 calendar-current-time-zone-cache) "EST"))
354 "Abbreviated name of standard time zone at `calendar-location-name'. 359 "Abbreviated name of standard time zone at `calendar-location-name'.
355For example, \"EST\" in New York City, \"PST\" for Los Angeles." 360For example, \"EST\" in New York City, \"PST\" for Los Angeles."
356 :type 'string 361 :type 'string
362 :version "28.1"
357 :group 'calendar-dst) 363 :group 'calendar-dst)
358 364
359(defcustom calendar-daylight-time-zone-name 365(defcustom calendar-daylight-time-zone-name
360 (or (nth 3 calendar-current-time-zone-cache) "EDT") 366 (if calendar-use-numeric-time-zones
367 (if calendar-current-time-zone-cache
368 (format-time-string
369 "%z" 0 (* 60 (cadr calendar-current-time-zone-cache)))
370 "+0000")
371 (or (nth 3 calendar-current-time-zone-cache) "EDT"))
361 "Abbreviated name of daylight saving time zone at `calendar-location-name'. 372 "Abbreviated name of daylight saving time zone at `calendar-location-name'.
362For example, \"EDT\" in New York City, \"PDT\" for Los Angeles." 373For example, \"EDT\" in New York City, \"PDT\" for Los Angeles."
363 :type 'string 374 :type 'string
375 :version "28.1"
364 :group 'calendar-dst) 376 :group 'calendar-dst)
365 377
366(defcustom calendar-daylight-savings-starts-time 378(defcustom calendar-daylight-savings-starts-time
diff --git a/lisp/calendar/calendar.el b/lisp/calendar/calendar.el
index 1d5b9479e2b..0efb2bc6607 100644
--- a/lisp/calendar/calendar.el
+++ b/lisp/calendar/calendar.el
@@ -1061,6 +1061,13 @@ calendar."
1061 :type 'boolean 1061 :type 'boolean
1062 :group 'holidays) 1062 :group 'holidays)
1063 1063
1064(defcustom calendar-use-numeric-time-zones nil
1065 "If nil, use symbolic time zones like \"CET\" when displaying dates.
1066If non-nil, use numeric time zones like \"+0100\"."
1067 :type 'boolean
1068 :version "28.1"
1069 :group 'calendar)
1070
1064;;; End of user options. 1071;;; End of user options.
1065 1072
1066(calendar-recompute-layout-variables) 1073(calendar-recompute-layout-variables)
diff --git a/lisp/calendar/solar.el b/lisp/calendar/solar.el
index 6a813e9ee82..635bdd8f11c 100644
--- a/lisp/calendar/solar.el
+++ b/lisp/calendar/solar.el
@@ -209,7 +209,6 @@ Returns nil if nothing was entered."
209 209
210(defun solar-setup () 210(defun solar-setup ()
211 "Prompt for `calendar-longitude', `calendar-latitude', `calendar-time-zone'." 211 "Prompt for `calendar-longitude', `calendar-latitude', `calendar-time-zone'."
212 (beep)
213 (or calendar-longitude 212 (or calendar-longitude
214 (setq calendar-longitude 213 (setq calendar-longitude
215 (solar-get-number 214 (solar-get-number
@@ -840,7 +839,9 @@ This function is suitable for execution in an init file."
840 "E" "W")))))) 839 "E" "W"))))))
841 (calendar-standard-time-zone-name 840 (calendar-standard-time-zone-name
842 (if (< arg 16) calendar-standard-time-zone-name 841 (if (< arg 16) calendar-standard-time-zone-name
843 (cond ((zerop calendar-time-zone) "UTC") 842 (cond ((zerop calendar-time-zone)
843 (if calendar-use-numeric-time-zones
844 "+0100" "UTC"))
844 ((< calendar-time-zone 0) 845 ((< calendar-time-zone 0)
845 (format "UTC%dmin" calendar-time-zone)) 846 (format "UTC%dmin" calendar-time-zone))
846 (t (format "UTC+%dmin" calendar-time-zone))))) 847 (t (format "UTC+%dmin" calendar-time-zone)))))
@@ -1013,7 +1014,10 @@ Requires floating point."
1013 (let* ((m displayed-month) 1014 (let* ((m displayed-month)
1014 (y displayed-year) 1015 (y displayed-year)
1015 (calendar-standard-time-zone-name 1016 (calendar-standard-time-zone-name
1016 (if calendar-time-zone calendar-standard-time-zone-name "UTC")) 1017 (cond
1018 (calendar-time-zone calendar-standard-time-zone-name)
1019 (calendar-use-numeric-time-zones "+0100")
1020 (t "UTC")))
1017 (calendar-daylight-savings-starts 1021 (calendar-daylight-savings-starts
1018 (if calendar-time-zone calendar-daylight-savings-starts)) 1022 (if calendar-time-zone calendar-daylight-savings-starts))
1019 (calendar-daylight-savings-ends 1023 (calendar-daylight-savings-ends
diff --git a/lisp/calendar/time-date.el b/lisp/calendar/time-date.el
index eeb09926a6e..125f9acc705 100644
--- a/lisp/calendar/time-date.el
+++ b/lisp/calendar/time-date.el
@@ -527,6 +527,21 @@ TIME is modified and returned."
527 527
528 time) 528 time)
529 529
530(defun decoded-time-period (time)
531 "Interpret DECODED as a period and return its length in seconds.
532For computational purposes, years are 365 days long and months
533are 30 days long."
534 (+ (if (consp (decoded-time-second time))
535 ;; Fractional second.
536 (/ (float (car (decoded-time-second time)))
537 (cdr (decoded-time-second time)))
538 (or (decoded-time-second time) 0))
539 (* (or (decoded-time-minute time) 0) 60)
540 (* (or (decoded-time-hour time) 0) 60 60)
541 (* (or (decoded-time-day time) 0) 60 60 24)
542 (* (or (decoded-time-month time) 0) 60 60 24 30)
543 (* (or (decoded-time-year time) 0) 60 60 24 365)))
544
530(provide 'time-date) 545(provide 'time-date)
531 546
532;;; time-date.el ends here 547;;; time-date.el ends here
diff --git a/lisp/cus-dep.el b/lisp/cus-dep.el
index e2c2ebe5f42..7c60916ee01 100644
--- a/lisp/cus-dep.el
+++ b/lisp/cus-dep.el
@@ -70,7 +70,7 @@ Usage: emacs -batch -l ./cus-dep.el -f custom-make-dependencies DIRS"
70 (directory-files subdir nil 70 (directory-files subdir nil
71 "\\`[^=.].*\\.el\\'")))) 71 "\\`[^=.].*\\.el\\'"))))
72 (progress (make-progress-reporter 72 (progress (make-progress-reporter
73 (byte-compile-info-string "Scanning files for custom") 73 (byte-compile-info "Scanning files for custom")
74 0 (length files) nil 10))) 74 0 (length files) nil 10)))
75 (with-temp-buffer 75 (with-temp-buffer
76 (dolist (elem files) 76 (dolist (elem files)
@@ -128,8 +128,8 @@ Usage: emacs -batch -l ./cus-dep.el -f custom-make-dependencies DIRS"
128 type))))))))))) 128 type)))))))))))
129 (error nil))))))) 129 (error nil)))))))
130 (progress-reporter-done progress)) 130 (progress-reporter-done progress))
131 (byte-compile-info-message "Generating %s..." 131 (byte-compile-info
132 generated-custom-dependencies-file) 132 (format "Generating %s..." generated-custom-dependencies-file) t)
133 (set-buffer (find-file-noselect generated-custom-dependencies-file)) 133 (set-buffer (find-file-noselect generated-custom-dependencies-file))
134 (setq buffer-undo-list t) 134 (setq buffer-undo-list t)
135 (erase-buffer) 135 (erase-buffer)
@@ -218,8 +218,8 @@ elements the files that have variables or faces that contain that
218version. These files should be loaded before showing the customization 218version. These files should be loaded before showing the customization
219buffer that `customize-changed-options' generates.\")\n\n")) 219buffer that `customize-changed-options' generates.\")\n\n"))
220 (save-buffer) 220 (save-buffer)
221 (byte-compile-info-message "Generating %s...done" 221 (byte-compile-info
222 generated-custom-dependencies-file)) 222 (format "Generating %s...done" generated-custom-dependencies-file) t))
223 223
224 224
225(provide 'cus-dep) 225(provide 'cus-dep)
diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el
index 1942f25e891..16695967dfa 100644
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ -4841,7 +4841,10 @@ The format is suitable for use with `easy-menu-define'."
4841 (error "You can't edit this part of the Custom buffer")) 4841 (error "You can't edit this part of the Custom buffer"))
4842 4842
4843(defun Custom-newline (pos &optional event) 4843(defun Custom-newline (pos &optional event)
4844 "Invoke button at POS, or refuse to allow editing of Custom buffer." 4844 "Invoke button at POS, or refuse to allow editing of Custom buffer.
4845
4846To see what function the widget will call, use the
4847`widget-describe' command."
4845 (interactive "@d") 4848 (interactive "@d")
4846 (let ((button (get-char-property pos 'button))) 4849 (let ((button (get-char-property pos 'button)))
4847 ;; If there is no button at point, then use the one at the start 4850 ;; If there is no button at point, then use the one at the start
diff --git a/lisp/custom.el b/lisp/custom.el
index 885c486c5e4..db7f6a056d4 100644
--- a/lisp/custom.el
+++ b/lisp/custom.el
@@ -1541,6 +1541,20 @@ Each of the arguments ARGS has this form:
1541This means reset VARIABLE. (The argument IGNORED is ignored)." 1541This means reset VARIABLE. (The argument IGNORED is ignored)."
1542 (apply #'custom-theme-reset-variables 'user args)) 1542 (apply #'custom-theme-reset-variables 'user args))
1543 1543
1544(defun custom-add-choice (variable choice)
1545 "Add CHOICE to the custom type of VARIABLE.
1546If a choice with the same tag already exists, no action is taken."
1547 (let ((choices (get variable 'custom-type)))
1548 (unless (eq (car choices) 'choice)
1549 (error "Not a choice type: %s" choices))
1550 (unless (seq-find (lambda (elem)
1551 (equal (caddr (member :tag elem))
1552 (caddr (member :tag choice))))
1553 (cdr choices))
1554 ;; Put the new choice at the end.
1555 (put variable 'custom-type
1556 (append choices (list choice))))))
1557
1544;;; The End. 1558;;; The End.
1545 1559
1546(provide 'custom) 1560(provide 'custom)
diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index efb214088d8..84d8c36f45f 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -688,7 +688,7 @@ are executed in the background on each file sequentially waiting
688for each command to terminate before running the next command. 688for each command to terminate before running the next command.
689In shell syntax this means separating the individual commands with `;'. 689In shell syntax this means separating the individual commands with `;'.
690 690
691The output appears in the buffer `*Async Shell Command*'." 691The output appears in the buffer `shell-command-buffer-name-async'."
692 (interactive 692 (interactive
693 (let ((files (dired-get-marked-files t current-prefix-arg nil nil t))) 693 (let ((files (dired-get-marked-files t current-prefix-arg nil nil t)))
694 (list 694 (list
@@ -727,7 +727,7 @@ it, write `*\"\"' in place of just `*'. This is equivalent to just
727 727
728If COMMAND ends in `&', `;', or `;&', it is executed in the 728If COMMAND ends in `&', `;', or `;&', it is executed in the
729background asynchronously, and the output appears in the buffer 729background asynchronously, and the output appears in the buffer
730`*Async Shell Command*'. When operating on multiple files and COMMAND 730`shell-command-buffer-name-async'. When operating on multiple files and COMMAND
731ends in `&', the shell command is executed on each file in parallel. 731ends in `&', the shell command is executed on each file in parallel.
732However, when COMMAND ends in `;' or `;&' then commands are executed 732However, when COMMAND ends in `;' or `;&' then commands are executed
733in the background on each file sequentially waiting for each command 733in the background on each file sequentially waiting for each command
@@ -735,7 +735,7 @@ to terminate before running the next command. You can also use
735`dired-do-async-shell-command' that automatically adds `&'. 735`dired-do-async-shell-command' that automatically adds `&'.
736 736
737Otherwise, COMMAND is executed synchronously, and the output 737Otherwise, COMMAND is executed synchronously, and the output
738appears in the buffer `*Shell Command Output*'. 738appears in the buffer `shell-command-buffer-name'.
739 739
740This feature does not try to redisplay Dired buffers afterward, as 740This feature does not try to redisplay Dired buffers afterward, as
741there's no telling what files COMMAND may have changed. 741there's no telling what files COMMAND may have changed.
@@ -952,13 +952,17 @@ With a prefix argument, kill that many lines starting with the current line.
952 "Kill all marked lines (not the files). 952 "Kill all marked lines (not the files).
953With a prefix argument, kill that many lines starting with the current line. 953With a prefix argument, kill that many lines starting with the current line.
954\(A negative argument kills backward.) 954\(A negative argument kills backward.)
955
955If you use this command with a prefix argument to kill the line 956If you use this command with a prefix argument to kill the line
956for a file that is a directory, which you have inserted in the 957for a file that is a directory, which you have inserted in the
957Dired buffer as a subdirectory, then it deletes that subdirectory 958Dired buffer as a subdirectory, then it deletes that subdirectory
958from the buffer as well. 959from the buffer as well.
960
959To kill an entire subdirectory \(without killing its line in the 961To kill an entire subdirectory \(without killing its line in the
960parent directory), go to its directory header line and use this 962parent directory), go to its directory header line and use this
961command with a prefix argument (the value does not matter)." 963command with a prefix argument (the value does not matter).
964
965To undo the killing, the undo command can be used as normally."
962 ;; Returns count of killed lines. FMT="" suppresses message. 966 ;; Returns count of killed lines. FMT="" suppresses message.
963 (interactive "P") 967 (interactive "P")
964 (if arg 968 (if arg
@@ -1010,8 +1014,8 @@ command with a prefix argument (the value does not matter)."
1010(defvar dired-compress-file-suffixes 1014(defvar dired-compress-file-suffixes
1011 '( 1015 '(
1012 ;; "tar -zxf" isn't used because it's not available on the 1016 ;; "tar -zxf" isn't used because it's not available on the
1013 ;; Solaris10 version of tar. Solaris10 becomes obsolete in 2021. 1017 ;; Solaris 10 version of tar (obsolete in 2024?).
1014 ;; Same thing on AIX 7.1. 1018 ;; Same thing on AIX 7.1 (obsolete 2023?) and 7.2 (obsolete 2022?).
1015 ("\\.tar\\.gz\\'" "" "gzip -dc %i | tar -xf -") 1019 ("\\.tar\\.gz\\'" "" "gzip -dc %i | tar -xf -")
1016 ("\\.tgz\\'" "" "gzip -dc %i | tar -xf -") 1020 ("\\.tgz\\'" "" "gzip -dc %i | tar -xf -")
1017 ("\\.gz\\'" "" "gunzip") 1021 ("\\.gz\\'" "" "gunzip")
@@ -1974,6 +1978,10 @@ Optional arg HOW-TO determines how to treat the target.
1974 (apply (car into-dir) operation rfn-list fn-list target (cdr into-dir)) 1978 (apply (car into-dir) operation rfn-list fn-list target (cdr into-dir))
1975 (if (not (or dired-one-file into-dir)) 1979 (if (not (or dired-one-file into-dir))
1976 (error "Marked %s: target must be a directory: %s" operation target)) 1980 (error "Marked %s: target must be a directory: %s" operation target))
1981 (if (and (not (file-directory-p (car fn-list)))
1982 (not (file-directory-p target))
1983 (directory-name-p target))
1984 (error "%s: Target directory does not exist: %s" operation target))
1977 ;; rename-file bombs when moving directories unless we do this: 1985 ;; rename-file bombs when moving directories unless we do this:
1978 (or into-dir (setq target (directory-file-name target))) 1986 (or into-dir (setq target (directory-file-name target)))
1979 (dired-create-files 1987 (dired-create-files
diff --git a/lisp/dired.el b/lisp/dired.el
index 1792250ac90..d19d6d1581d 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -125,7 +125,7 @@ For more details, see Info node `(emacs)ls in Lisp'."
125 "Informs Dired about how `ls -lF' marks symbolic links. 125 "Informs Dired about how `ls -lF' marks symbolic links.
126Set this to t if `ls' (or whatever program is specified by 126Set this to t if `ls' (or whatever program is specified by
127`insert-directory-program') with `-lF' marks the symbolic link 127`insert-directory-program') with `-lF' marks the symbolic link
128itself with a trailing @ (usually the case under Ultrix). 128itself with a trailing @ (usually the case under Ultrix and macOS).
129 129
130Example: if `ln -s foo bar; ls -F bar' gives `bar -> foo', set it to 130Example: if `ln -s foo bar; ls -F bar' gives `bar -> foo', set it to
131nil (the default), if it gives `bar@ -> foo', set it to t. 131nil (the default), if it gives `bar@ -> foo', set it to t.
diff --git a/lisp/emacs-lisp/autoload.el b/lisp/emacs-lisp/autoload.el
index d9da36586ce..05eb0ac5693 100644
--- a/lisp/emacs-lisp/autoload.el
+++ b/lisp/emacs-lisp/autoload.el
@@ -1126,7 +1126,7 @@ write its autoloads into the specified file instead."
1126 ;; Elements remaining in FILES have no existing autoload sections yet. 1126 ;; Elements remaining in FILES have no existing autoload sections yet.
1127 (let ((no-autoloads-time (or last-time '(0 0 0 0))) 1127 (let ((no-autoloads-time (or last-time '(0 0 0 0)))
1128 (progress (make-progress-reporter 1128 (progress (make-progress-reporter
1129 (byte-compile-info-string 1129 (byte-compile-info
1130 (concat "Scraping files for " 1130 (concat "Scraping files for "
1131 (file-relative-name 1131 (file-relative-name
1132 generated-autoload-file))) 1132 generated-autoload-file)))
@@ -1169,6 +1169,19 @@ write its autoloads into the specified file instead."
1169 ;; file-local autoload-generated-file settings. 1169 ;; file-local autoload-generated-file settings.
1170 (autoload-save-buffers)))) 1170 (autoload-save-buffers))))
1171 1171
1172(defun batch-update-autoloads--summary (strings)
1173 (let ((message ""))
1174 (while strings
1175 (when (> (length (concat message " " (car strings))) 64)
1176 (byte-compile-info (concat message " ...") t "SCRAPE")
1177 (setq message ""))
1178 (setq message (if (zerop (length message))
1179 (car strings)
1180 (concat message " " (car strings))))
1181 (setq strings (cdr strings)))
1182 (when (> (length message) 0)
1183 (byte-compile-info message t "SCRAPE"))))
1184
1172;;;###autoload 1185;;;###autoload
1173(defun batch-update-autoloads () 1186(defun batch-update-autoloads ()
1174 "Update loaddefs.el autoloads in batch mode. 1187 "Update loaddefs.el autoloads in batch mode.
@@ -1192,6 +1205,7 @@ should be non-nil)."
1192 (or (string-match "\\`site-" file) 1205 (or (string-match "\\`site-" file)
1193 (push (expand-file-name file) autoload-excludes))))))) 1206 (push (expand-file-name file) autoload-excludes)))))))
1194 (let ((args command-line-args-left)) 1207 (let ((args command-line-args-left))
1208 (batch-update-autoloads--summary args)
1195 (setq command-line-args-left nil) 1209 (setq command-line-args-left nil)
1196 (apply #'update-directory-autoloads args))) 1210 (apply #'update-directory-autoloads args)))
1197 1211
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index 0d9c449b3b4..4987596bf95 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -648,14 +648,23 @@
648 (setq args (cons (car rest) args))) 648 (setq args (cons (car rest) args)))
649 (setq rest (cdr rest))) 649 (setq rest (cdr rest)))
650 (if (cdr constants) 650 (if (cdr constants)
651 (if args 651 (let ((const (apply (car form) (nreverse constants))))
652 (list (car form) 652 (if args
653 (apply (car form) constants) 653 (append (list (car form) const)
654 (if (cdr args) 654 (nreverse args))
655 (cons (car form) (nreverse args)) 655 const))
656 (car args))) 656 form)))
657 (apply (car form) constants)) 657
658 form))) 658(defun byte-optimize-min-max (form)
659 "Optimize `min' and `max'."
660 (let ((opt (byte-optimize-associative-math form)))
661 (if (and (consp opt) (memq (car opt) '(min max))
662 (= (length opt) 4))
663 ;; (OP x y z) -> (OP (OP x y) z), in order to use binary byte ops.
664 (list (car opt)
665 (list (car opt) (nth 1 opt) (nth 2 opt))
666 (nth 3 opt))
667 opt)))
659 668
660;; Use OP to reduce any leading prefix of constant numbers in the list 669;; Use OP to reduce any leading prefix of constant numbers in the list
661;; (cons ACCUM ARGS) down to a single number, and return the 670;; (cons ACCUM ARGS) down to a single number, and return the
@@ -878,8 +887,8 @@
878(put '* 'byte-optimizer #'byte-optimize-multiply) 887(put '* 'byte-optimizer #'byte-optimize-multiply)
879(put '- 'byte-optimizer #'byte-optimize-minus) 888(put '- 'byte-optimizer #'byte-optimize-minus)
880(put '/ 'byte-optimizer #'byte-optimize-divide) 889(put '/ 'byte-optimizer #'byte-optimize-divide)
881(put 'max 'byte-optimizer #'byte-optimize-associative-math) 890(put 'max 'byte-optimizer #'byte-optimize-min-max)
882(put 'min 'byte-optimizer #'byte-optimize-associative-math) 891(put 'min 'byte-optimizer #'byte-optimize-min-max)
883 892
884(put '= 'byte-optimizer #'byte-optimize-binary-predicate) 893(put '= 'byte-optimizer #'byte-optimize-binary-predicate)
885(put 'eq 'byte-optimizer #'byte-optimize-binary-predicate) 894(put 'eq 'byte-optimizer #'byte-optimize-binary-predicate)
diff --git a/lisp/emacs-lisp/byte-run.el b/lisp/emacs-lisp/byte-run.el
index 539846683f0..8c16c172bed 100644
--- a/lisp/emacs-lisp/byte-run.el
+++ b/lisp/emacs-lisp/byte-run.el
@@ -587,13 +587,26 @@ Otherwise, return nil. For internal use only."
587 (mapconcat (lambda (char) (format "`?\\%c'" char)) 587 (mapconcat (lambda (char) (format "`?\\%c'" char))
588 sorted ", "))))) 588 sorted ", ")))))
589 589
590(defun byte-compile-info (string &optional message type)
591 "Format STRING in a way that looks pleasing in the compilation output.
592If MESSAGE, output the message, too.
593
594If TYPE, it should be a string that says what the information
595type is. This defaults to \"INFO\"."
596 (let ((string (format " %-9s%s" (or type "INFO") string)))
597 (when message
598 (message "%s" string))
599 string))
600
590(defun byte-compile-info-string (&rest args) 601(defun byte-compile-info-string (&rest args)
591 "Format ARGS in a way that looks pleasing in the compilation output." 602 "Format ARGS in a way that looks pleasing in the compilation output."
592 (format " %-9s%s" "INFO" (apply #'format args))) 603 (declare (obsolete byte-compile-info "28.1"))
604 (byte-compile-info (apply #'format args)))
593 605
594(defun byte-compile-info-message (&rest args) 606(defun byte-compile-info-message (&rest args)
595 "Message format ARGS in a way that looks pleasing in the compilation output." 607 "Message format ARGS in a way that looks pleasing in the compilation output."
596 (message "%s" (apply #'byte-compile-info-string args))) 608 (declare (obsolete byte-compile-info "28.1"))
609 (byte-compile-info (apply #'format args) t))
597 610
598 611
599;; I nuked this because it's not a good idea for users to think of using it. 612;; I nuked this because it's not a good idea for users to think of using it.
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index 7a56aa2df29..c5b086f91a0 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -3659,10 +3659,10 @@ If it is nil, then the handler is \"byte-compile-SYMBOL.\""
3659(byte-defop-compiler (% byte-rem) 2) 3659(byte-defop-compiler (% byte-rem) 2)
3660(byte-defop-compiler aset 3) 3660(byte-defop-compiler aset 3)
3661 3661
3662(byte-defop-compiler max byte-compile-associative) 3662(byte-defop-compiler max byte-compile-min-max)
3663(byte-defop-compiler min byte-compile-associative) 3663(byte-defop-compiler min byte-compile-min-max)
3664(byte-defop-compiler (+ byte-plus) byte-compile-associative) 3664(byte-defop-compiler (+ byte-plus) byte-compile-variadic-numeric)
3665(byte-defop-compiler (* byte-mult) byte-compile-associative) 3665(byte-defop-compiler (* byte-mult) byte-compile-variadic-numeric)
3666 3666
3667;;####(byte-defop-compiler move-to-column 1) 3667;;####(byte-defop-compiler move-to-column 1)
3668(byte-defop-compiler-1 interactive byte-compile-noop) 3668(byte-defop-compiler-1 interactive byte-compile-noop)
@@ -3809,30 +3809,36 @@ discarding."
3809 (if byte-compile--for-effect (setq byte-compile--for-effect nil) 3809 (if byte-compile--for-effect (setq byte-compile--for-effect nil)
3810 (byte-compile-out 'byte-constant (nth 1 form)))) 3810 (byte-compile-out 'byte-constant (nth 1 form))))
3811 3811
3812;; Compile a function that accepts one or more args and is right-associative. 3812;; Compile a pure function that accepts zero or more numeric arguments
3813;; We do it by left-associativity so that the operations 3813;; and has an opcode for the binary case.
3814;; are done in the same order as in interpreted code. 3814;; Single-argument calls are assumed to be numeric identity and are
3815;; We treat the one-arg case, as in (+ x), like (+ x 0). 3815;; compiled as (* x 1) in order to convert markers to numbers and
3816;; in order to convert markers to numbers, and trigger expected errors. 3816;; trigger type errors.
3817(defun byte-compile-associative (form) 3817(defun byte-compile-variadic-numeric (form)
3818 (pcase (length form)
3819 (1
3820 ;; No args: use the identity value for the operation.
3821 (byte-compile-constant (eval form)))
3822 (2
3823 ;; One arg: compile (OP x) as (* x 1). This is identity for
3824 ;; all numerical values including -0.0, infinities and NaNs.
3825 (byte-compile-form (nth 1 form))
3826 (byte-compile-constant 1)
3827 (byte-compile-out (get '* 'byte-opcode) 0))
3828 (3
3829 (byte-compile-form (nth 1 form))
3830 (byte-compile-form (nth 2 form))
3831 (byte-compile-out (get (car form) 'byte-opcode) 0))
3832 (_
3833 ;; >2 args: compile as a single function call.
3834 (byte-compile-normal-call form))))
3835
3836(defun byte-compile-min-max (form)
3837 "Byte-compile calls to `min' or `max'."
3818 (if (cdr form) 3838 (if (cdr form)
3819 (let ((opcode (get (car form) 'byte-opcode)) 3839 (byte-compile-variadic-numeric form)
3820 args) 3840 ;; No args: warn and emit code that raises an error when executed.
3821 (if (and (< 3 (length form)) 3841 (byte-compile-normal-call form)))
3822 (memq opcode (list (get '+ 'byte-opcode)
3823 (get '* 'byte-opcode))))
3824 ;; Don't use binary operations for > 2 operands, as that
3825 ;; may cause overflow/truncation in float operations.
3826 (byte-compile-normal-call form)
3827 (setq args (copy-sequence (cdr form)))
3828 (byte-compile-form (car args))
3829 (setq args (cdr args))
3830 (or args (setq args '(0)
3831 opcode (get '+ 'byte-opcode)))
3832 (dolist (arg args)
3833 (byte-compile-form arg)
3834 (byte-compile-out opcode 0))))
3835 (byte-compile-constant (eval form))))
3836 3842
3837 3843
3838;; more complicated compiler macros 3844;; more complicated compiler macros
@@ -3847,7 +3853,7 @@ discarding."
3847(byte-defop-compiler indent-to) 3853(byte-defop-compiler indent-to)
3848(byte-defop-compiler insert) 3854(byte-defop-compiler insert)
3849(byte-defop-compiler-1 function byte-compile-function-form) 3855(byte-defop-compiler-1 function byte-compile-function-form)
3850(byte-defop-compiler-1 - byte-compile-minus) 3856(byte-defop-compiler (- byte-diff) byte-compile-minus)
3851(byte-defop-compiler (/ byte-quo) byte-compile-quo) 3857(byte-defop-compiler (/ byte-quo) byte-compile-quo)
3852(byte-defop-compiler nconc) 3858(byte-defop-compiler nconc)
3853 3859
@@ -3914,30 +3920,17 @@ discarding."
3914 ((byte-compile-normal-call form))))) 3920 ((byte-compile-normal-call form)))))
3915 3921
3916(defun byte-compile-minus (form) 3922(defun byte-compile-minus (form)
3917 (let ((len (length form))) 3923 (if (/= (length form) 2)
3918 (cond 3924 (byte-compile-variadic-numeric form)
3919 ((= 1 len) (byte-compile-constant 0)) 3925 (byte-compile-form (cadr form))
3920 ((= 2 len) 3926 (byte-compile-out 'byte-negate 0)))
3921 (byte-compile-form (cadr form))
3922 (byte-compile-out 'byte-negate 0))
3923 ((= 3 len)
3924 (byte-compile-form (nth 1 form))
3925 (byte-compile-form (nth 2 form))
3926 (byte-compile-out 'byte-diff 0))
3927 ;; Don't use binary operations for > 2 operands, as that may
3928 ;; cause overflow/truncation in float operations.
3929 (t (byte-compile-normal-call form)))))
3930 3927
3931(defun byte-compile-quo (form) 3928(defun byte-compile-quo (form)
3932 (let ((len (length form))) 3929 (if (= (length form) 3)
3933 (cond ((< len 2) 3930 (byte-compile-two-args form)
3934 (byte-compile-subr-wrong-args form "1 or more")) 3931 ;; N-ary `/' is not the left-reduction of binary `/' because if any
3935 ((= len 3) 3932 ;; argument is a float, then everything is done in floating-point.
3936 (byte-compile-two-args form)) 3933 (byte-compile-normal-call form)))
3937 (t
3938 ;; Don't use binary operations for > 2 operands, as that
3939 ;; may cause overflow/truncation in float operations.
3940 (byte-compile-normal-call form)))))
3941 3934
3942(defun byte-compile-nconc (form) 3935(defun byte-compile-nconc (form)
3943 (let ((len (length form))) 3936 (let ((len (length form)))
diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el
index 4e8423eb5b1..02da07daaf4 100644
--- a/lisp/emacs-lisp/cl-generic.el
+++ b/lisp/emacs-lisp/cl-generic.el
@@ -211,7 +211,16 @@ DEFAULT-BODY, if present, is used as the body of a default method.
211 [&rest [&or 211 [&rest [&or
212 ("declare" &rest sexp) 212 ("declare" &rest sexp)
213 (":argument-precedence-order" &rest sexp) 213 (":argument-precedence-order" &rest sexp)
214 (&define ":method" [&rest atom] 214 (&define ":method"
215 ;; FIXME: The `:unique'
216 ;; construct works around
217 ;; Bug#42672. We'd rather want
218 ;; names like those generated by
219 ;; `cl-defmethod', but that
220 ;; requires larger changes to
221 ;; Edebug.
222 :unique "cl-generic-:method@"
223 [&rest cl-generic-method-qualifier]
215 cl-generic-method-args lambda-doc 224 cl-generic-method-args lambda-doc
216 def-body)]] 225 def-body)]]
217 def-body))) 226 def-body)))
@@ -432,9 +441,8 @@ The set of acceptable TYPEs (also called \"specializers\") is defined
432 (&define ; this means we are defining something 441 (&define ; this means we are defining something
433 [&or name ("setf" name :name setf)] 442 [&or name ("setf" name :name setf)]
434 ;; ^^ This is the methods symbol 443 ;; ^^ This is the methods symbol
435 [ &rest atom ] ; Multiple qualifiers are allowed. 444 [ &rest cl-generic-method-qualifier ]
436 ; Like in CLOS spec, we support 445 ;; Multiple qualifiers are allowed.
437 ; any non-list values.
438 cl-generic-method-args ; arguments 446 cl-generic-method-args ; arguments
439 lambda-doc ; documentation string 447 lambda-doc ; documentation string
440 def-body))) ; part to be debugged 448 def-body))) ; part to be debugged
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index 6c1426ce5cb..c38019d4a73 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -2016,7 +2016,12 @@ info node `(cl) Function Bindings' for details.
2016 2016
2017\(fn ((FUNC ARGLIST BODY...) ...) FORM...)" 2017\(fn ((FUNC ARGLIST BODY...) ...) FORM...)"
2018 (declare (indent 1) 2018 (declare (indent 1)
2019 (debug ((&rest [&or (&define name function-form) (cl-defun)]) 2019 (debug ((&rest [&or (&define name :unique "cl-flet@" function-form)
2020 (&define name :unique "cl-flet@"
2021 cl-lambda-list
2022 cl-declarations-or-string
2023 [&optional ("interactive" interactive)]
2024 def-body)])
2020 cl-declarations body))) 2025 cl-declarations body)))
2021 (let ((binds ()) (newenv macroexpand-all-environment)) 2026 (let ((binds ()) (newenv macroexpand-all-environment))
2022 (dolist (binding bindings) 2027 (dolist (binding bindings)
diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el
index a565e8f6dcb..d9bbf6129c6 100644
--- a/lisp/emacs-lisp/edebug.el
+++ b/lisp/emacs-lisp/edebug.el
@@ -1240,6 +1240,13 @@ purpose by adding an entry to this alist, and setting
1240 ;; since it wraps the list of forms with a call to `edebug-enter'. 1240 ;; since it wraps the list of forms with a call to `edebug-enter'.
1241 ;; Uses the dynamically bound vars edebug-def-name and edebug-def-args. 1241 ;; Uses the dynamically bound vars edebug-def-name and edebug-def-args.
1242 ;; Do this after parsing since that may find a name. 1242 ;; Do this after parsing since that may find a name.
1243 (when (string-match-p (rx bos "edebug-anon" (+ digit) eos)
1244 (symbol-name edebug-old-def-name))
1245 ;; FIXME: Due to Bug#42701, we reset an anonymous name so that
1246 ;; backtracking doesn't generate duplicate definitions. It would
1247 ;; be better to not define wrappers in the case of a non-matching
1248 ;; specification branch to begin with.
1249 (setq edebug-old-def-name nil))
1243 (setq edebug-def-name 1250 (setq edebug-def-name
1244 (or edebug-def-name edebug-old-def-name (gensym "edebug-anon"))) 1251 (or edebug-def-name edebug-old-def-name (gensym "edebug-anon")))
1245 `(edebug-enter 1252 `(edebug-enter
@@ -1725,12 +1732,15 @@ contains a circular object."
1725 (&define . edebug-match-&define) 1732 (&define . edebug-match-&define)
1726 (name . edebug-match-name) 1733 (name . edebug-match-name)
1727 (:name . edebug-match-colon-name) 1734 (:name . edebug-match-colon-name)
1735 (:unique . edebug-match-:unique)
1728 (arg . edebug-match-arg) 1736 (arg . edebug-match-arg)
1729 (def-body . edebug-match-def-body) 1737 (def-body . edebug-match-def-body)
1730 (def-form . edebug-match-def-form) 1738 (def-form . edebug-match-def-form)
1731 ;; Less frequently used: 1739 ;; Less frequently used:
1732 ;; (function . edebug-match-function) 1740 ;; (function . edebug-match-function)
1733 (lambda-expr . edebug-match-lambda-expr) 1741 (lambda-expr . edebug-match-lambda-expr)
1742 (cl-generic-method-qualifier
1743 . edebug-match-cl-generic-method-qualifier)
1734 (cl-generic-method-args . edebug-match-cl-generic-method-args) 1744 (cl-generic-method-args . edebug-match-cl-generic-method-args)
1735 (cl-macrolet-expr . edebug-match-cl-macrolet-expr) 1745 (cl-macrolet-expr . edebug-match-cl-macrolet-expr)
1736 (cl-macrolet-name . edebug-match-cl-macrolet-name) 1746 (cl-macrolet-name . edebug-match-cl-macrolet-name)
@@ -2035,6 +2045,27 @@ contains a circular object."
2035 spec)) 2045 spec))
2036 nil) 2046 nil)
2037 2047
2048(defun edebug-match-:unique (_cursor spec)
2049 "Match a `:unique PREFIX' specifier.
2050SPEC is the symbol name prefix for `gensym'."
2051 (let ((suffix (gensym spec)))
2052 (setq edebug-def-name
2053 (if edebug-def-name
2054 ;; Construct a new name by appending to previous name.
2055 (intern (format "%s@%s" edebug-def-name suffix))
2056 suffix)))
2057 nil)
2058
2059(defun edebug-match-cl-generic-method-qualifier (cursor)
2060 "Match a QUALIFIER for `cl-defmethod' at CURSOR."
2061 (let ((args (edebug-top-element-required cursor "Expected qualifier")))
2062 ;; Like in CLOS spec, we support any non-list values.
2063 (unless (atom args) (edebug-no-match cursor "Atom expected"))
2064 ;; Append the arguments to `edebug-def-name' (Bug#42671).
2065 (setq edebug-def-name (intern (format "%s %s" edebug-def-name args)))
2066 (edebug-move-cursor cursor)
2067 (list args)))
2068
2038(defun edebug-match-cl-generic-method-args (cursor) 2069(defun edebug-match-cl-generic-method-args (cursor)
2039 (let ((args (edebug-top-element-required cursor "Expected arguments"))) 2070 (let ((args (edebug-top-element-required cursor "Expected arguments")))
2040 (if (not (consp args)) 2071 (if (not (consp args))
diff --git a/lisp/emacs-lisp/hierarchy.el b/lisp/emacs-lisp/hierarchy.el
new file mode 100644
index 00000000000..8cef029c4cf
--- /dev/null
+++ b/lisp/emacs-lisp/hierarchy.el
@@ -0,0 +1,579 @@
1;;; hierarchy.el --- Library to create and display hierarchy structures -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2020 Free Software Foundation, Inc.
4
5;; Author: Damien Cassou <damien@cassou.me>
6;; Maintainer: emacs-devel@gnu.org
7
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software: you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
21;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
22
23;;; Commentary:
24
25;; Library to create, query, navigate and display hierarchy structures.
26
27;; Creation: After having created a hierarchy with `hierarchy-new',
28;; populate it by calling `hierarchy-add-tree' or
29;; `hierarchy-add-trees'. You can then optionally sort its element
30;; with `hierarchy-sort'.
31
32;; Querying: You can learn more about your hierarchy by using
33;; functions such as `hierarchy-roots', `hierarchy-has-item',
34;; `hierarchy-length', `hierarchy-parent', `hierarchy-descendant-p'.
35
36;; Navigation: When your hierarchy is ready, you can use
37;; `hierarchy-map-item', `hierarchy-map', and `map-tree' to apply
38;; functions to elements of the hierarchy.
39
40;; Display: You can display a hierarchy as a tabulated list using
41;; `hierarchy-tabulated-display' and as an expandable/foldable tree
42;; using `hierarchy-convert-to-tree-widget'. The
43;; `hierarchy-labelfn-*' functions will help you display each item of
44;; the hierarchy the way you want it.
45
46;;; Limitation:
47
48;; - Current implementation uses #'equal to find and distinguish
49;; elements. Support for user-provided equality definition is
50;; desired but not yet implemented;
51;;
52;; - nil can't be added to a hierarchy;
53;;
54;; - the hierarchy is computed eagerly.
55
56;;; Code:
57
58(require 'seq)
59(require 'map)
60(require 'subr-x)
61(require 'cl-lib)
62
63
64;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
65;; Helpers
66;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
67
68(cl-defstruct (hierarchy
69 (:constructor hierarchy--make)
70 (:conc-name hierarchy--))
71 (roots (list)) ; list of the hierarchy roots (no parent)
72 (parents (make-hash-table :test 'equal)) ; map an item to its parent
73 (children (make-hash-table :test 'equal)) ; map an item to its childre
74 ;; cache containing the set of all items in the hierarchy
75 (seen-items (make-hash-table :test 'equal))) ; map an item to t
76
77(defun hierarchy--seen-items-add (hierarchy item)
78 "In HIERARCHY, add ITEM to seen items."
79 (map-put! (hierarchy--seen-items hierarchy) item t))
80
81(defun hierarchy--compute-roots (hierarchy)
82 "Search roots of HIERARCHY and return them."
83 (cl-set-difference
84 (map-keys (hierarchy--seen-items hierarchy))
85 (map-keys (hierarchy--parents hierarchy))
86 :test #'equal))
87
88(defun hierarchy--sort-roots (hierarchy sortfn)
89 "Compute, sort and store the roots of HIERARCHY.
90
91SORTFN is a function taking two items of the hierarchy as parameter and
92returning non-nil if the first parameter is lower than the second."
93 (setf (hierarchy--roots hierarchy)
94 (sort (hierarchy--compute-roots hierarchy)
95 sortfn)))
96
97(defun hierarchy--add-relation (hierarchy item parent acceptfn)
98 "In HIERARCHY, add ITEM as child of PARENT.
99
100ACCEPTFN is a function returning non-nil if its parameter (any object)
101should be an item of the hierarchy."
102 (let* ((existing-parent (hierarchy-parent hierarchy item))
103 (has-parent-p (funcall acceptfn existing-parent)))
104 (cond
105 ((and has-parent-p (not (equal existing-parent parent)))
106 (error "An item (%s) can only have one parent: '%s' vs '%s'"
107 item existing-parent parent))
108 ((not has-parent-p)
109 (let ((existing-children (map-elt (hierarchy--children hierarchy)
110 parent (list))))
111 (map-put! (hierarchy--children hierarchy)
112 parent (append existing-children (list item))))
113 (map-put! (hierarchy--parents hierarchy) item parent)))))
114
115(defun hierarchy--set-equal (list1 list2 &rest cl-keys)
116 "Return non-nil if LIST1 and LIST2 have same elements.
117
118I.e., if every element of LIST1 also appears in LIST2 and if
119every element of LIST2 also appears in LIST1.
120
121CL-KEYS are key-value pairs just like in `cl-subsetp'. Supported
122keys are :key and :test."
123 (and (apply 'cl-subsetp list1 list2 cl-keys)
124 (apply 'cl-subsetp list2 list1 cl-keys)))
125
126
127;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
128;; Creation
129;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
130
131(defun hierarchy-new ()
132 "Create a hierarchy and return it."
133 (hierarchy--make))
134
135(defun hierarchy-add-tree (hierarchy item parentfn &optional childrenfn acceptfn)
136 "In HIERARCHY, add ITEM.
137
138PARENTFN is either nil or a function defining the child-to-parent
139relationship: this function takes an item as parameter and should return
140the parent of this item in the hierarchy. If the item has no parent in the
141hierarchy (i.e., it should be a root), the function should return an object
142not accepted by acceptfn (i.e., nil for the default value of acceptfn).
143
144CHILDRENFN is either nil or a function defining the parent-to-children
145relationship: this function takes an item as parameter and should return a
146list of children of this item in the hierarchy.
147
148If both PARENTFN and CHILDRENFN are non-nil, the results of PARENTFN and
149CHILDRENFN are expected to be coherent with each other.
150
151ACCEPTFN is a function returning non-nil if its parameter (any object)
152should be an item of the hierarchy. By default, ACCEPTFN returns non-nil
153if its parameter is non-nil."
154 (unless (hierarchy-has-item hierarchy item)
155 (let ((acceptfn (or acceptfn #'identity)))
156 (hierarchy--seen-items-add hierarchy item)
157 (let ((parent (and parentfn (funcall parentfn item))))
158 (when (funcall acceptfn parent)
159 (hierarchy--add-relation hierarchy item parent acceptfn)
160 (hierarchy-add-tree hierarchy parent parentfn childrenfn)))
161 (let ((children (and childrenfn (funcall childrenfn item))))
162 (mapc (lambda (child)
163 (when (funcall acceptfn child)
164 (hierarchy--add-relation hierarchy child item acceptfn)
165 (hierarchy-add-tree hierarchy child parentfn childrenfn)))
166 children)))))
167
168(defun hierarchy-add-trees (hierarchy items parentfn &optional childrenfn acceptfn)
169 "Call `hierarchy-add-tree' on HIERARCHY and each element of ITEMS.
170
171PARENTFN, CHILDRENFN and ACCEPTFN have the same meaning as in `hierarchy-add'."
172 (seq-map (lambda (item)
173 (hierarchy-add-tree hierarchy item parentfn childrenfn acceptfn))
174 items))
175
176(defun hierarchy-add-list (hierarchy list &optional wrap childrenfn)
177 "Add to HIERARCHY the sub-lists in LIST.
178
179If WRAP is non-nil, allow duplicate items in LIST by wraping each
180item in a cons (id . item). The root's id is 1.
181
182CHILDRENFN is a function (defaults to `cdr') taking LIST as a
183parameter which should return LIST's children (a list). Each
184child is (recursively) passed as a parameter to CHILDRENFN to get
185its own children. Because of this parameter, LIST can be
186anything, not necessarily a list."
187 (let* ((childrenfn (or childrenfn #'cdr))
188 (id 0)
189 (wrapfn (lambda (item)
190 (if wrap
191 (cons (setq id (1+ id)) item)
192 item)))
193 (unwrapfn (if wrap #'cdr #'identity)))
194 (hierarchy-add-tree
195 hierarchy (funcall wrapfn list) nil
196 (lambda (item)
197 (mapcar wrapfn (funcall childrenfn
198 (funcall unwrapfn item)))))
199 hierarchy))
200
201(defun hierarchy-from-list (list &optional wrap childrenfn)
202 "Create and return a hierarchy built from LIST.
203
204This function passes LIST, WRAP and CHILDRENFN unchanged to
205`hierarchy-add-list'."
206 (hierarchy-add-list (hierarchy-new) list wrap childrenfn))
207
208(defun hierarchy-sort (hierarchy &optional sortfn)
209 "Modify HIERARCHY so that its roots and item's children are sorted.
210
211SORTFN is a function taking two items of the hierarchy as parameter and
212returning non-nil if the first parameter is lower than the second. By
213default, SORTFN is `string-lessp'."
214 (let ((sortfn (or sortfn #'string-lessp)))
215 (hierarchy--sort-roots hierarchy sortfn)
216 (mapc (lambda (parent)
217 (setf
218 (map-elt (hierarchy--children hierarchy) parent)
219 (sort (map-elt (hierarchy--children hierarchy) parent) sortfn)))
220 (map-keys (hierarchy--children hierarchy)))))
221
222(defun hierarchy-extract-tree (hierarchy item)
223 "Return a copy of HIERARCHY with ITEM's descendants and parents."
224 (if (not (hierarchy-has-item hierarchy item))
225 nil
226 (let ((tree (hierarchy-new)))
227 (hierarchy-add-tree tree item
228 (lambda (each) (hierarchy-parent hierarchy each))
229 (lambda (each)
230 (when (or (equal each item)
231 (hierarchy-descendant-p hierarchy each item))
232 (hierarchy-children hierarchy each))))
233 tree)))
234
235(defun hierarchy-copy (hierarchy)
236 "Return a copy of HIERARCHY.
237
238Items in HIERARCHY are shared, but structure is not."
239 (hierarchy-map-hierarchy (lambda (item _) (identity item)) hierarchy))
240
241
242;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
243;; Querying
244;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
245
246(defun hierarchy-items (hierarchy)
247 "Return a list of all items of HIERARCHY."
248 (map-keys (hierarchy--seen-items hierarchy)))
249
250(defun hierarchy-has-item (hierarchy item)
251 "Return t if HIERARCHY includes ITEM."
252 (map-contains-key (hierarchy--seen-items hierarchy) item))
253
254(defun hierarchy-empty-p (hierarchy)
255 "Return t if HIERARCHY is empty."
256 (= 0 (hierarchy-length hierarchy)))
257
258(defun hierarchy-length (hierarchy)
259 "Return the number of items in HIERARCHY."
260 (hash-table-count (hierarchy--seen-items hierarchy)))
261
262(defun hierarchy-has-root (hierarchy item)
263 "Return t if one of HIERARCHY's roots is ITEM.
264
265A root is an item with no parent."
266 (seq-contains-p (hierarchy-roots hierarchy) item))
267
268(defun hierarchy-roots (hierarchy)
269 "Return all roots of HIERARCHY.
270
271A root is an item with no parent."
272 (let ((roots (hierarchy--roots hierarchy)))
273 (or roots
274 (hierarchy--compute-roots hierarchy))))
275
276(defun hierarchy-leafs (hierarchy &optional node)
277 "Return all leafs of HIERARCHY.
278
279A leaf is an item with no child.
280
281If NODE is an item of HIERARCHY, only return leafs under NODE."
282 (let ((leafs (cl-set-difference
283 (map-keys (hierarchy--seen-items hierarchy))
284 (map-keys (hierarchy--children hierarchy)))))
285 (if (hierarchy-has-item hierarchy node)
286 (seq-filter (lambda (item)
287 (hierarchy-descendant-p hierarchy item node))
288 leafs)
289 leafs)))
290
291(defun hierarchy-parent (hierarchy item)
292 "In HIERARCHY, return parent of ITEM."
293 (map-elt (hierarchy--parents hierarchy) item))
294
295(defun hierarchy-children (hierarchy parent)
296 "In HIERARCHY, return children of PARENT."
297 (map-elt (hierarchy--children hierarchy) parent (list)))
298
299(defun hierarchy-child-p (hierarchy item1 item2)
300 "In HIERARCHY, return non-nil if and only if ITEM1 is a child of ITEM2."
301 (equal (hierarchy-parent hierarchy item1) item2))
302
303(defun hierarchy-descendant-p (hierarchy item1 item2)
304 "In HIERARCHY, return non-nil if and only if ITEM1 is a descendant of ITEM2.
305
306ITEM1 is a descendant of ITEM2 if and only if both are items of HIERARCHY
307and either:
308
309- ITEM1 is child of ITEM2, or
310- ITEM1's parent is a descendant of ITEM2."
311 (and
312 (hierarchy-has-item hierarchy item1)
313 (hierarchy-has-item hierarchy item2)
314 (or
315 (hierarchy-child-p hierarchy item1 item2)
316 (hierarchy-descendant-p
317 hierarchy (hierarchy-parent hierarchy item1) item2))))
318
319(defun hierarchy-equal (hierarchy1 hierarchy2)
320 "Return t if HIERARCHY1 and HIERARCHY2 are equal.
321
322Two equal hierarchies share the same items and the same
323relationships among them."
324 (and (hierarchy-p hierarchy1)
325 (hierarchy-p hierarchy2)
326 (= (hierarchy-length hierarchy1) (hierarchy-length hierarchy2))
327 ;; parents are the same
328 (seq-every-p (lambda (child)
329 (equal (hierarchy-parent hierarchy1 child)
330 (hierarchy-parent hierarchy2 child)))
331 (map-keys (hierarchy--parents hierarchy1)))
332 ;; children are the same
333 (seq-every-p (lambda (parent)
334 (hierarchy--set-equal
335 (hierarchy-children hierarchy1 parent)
336 (hierarchy-children hierarchy2 parent)
337 :test #'equal))
338 (map-keys (hierarchy--children hierarchy1)))))
339
340
341;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
342;; Navigation
343;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
344
345(defun hierarchy-map-item (func item hierarchy &optional indent)
346 "Return the result of applying FUNC to ITEM and its descendants in HIERARCHY.
347
348This function navigates the tree top-down: FUNCTION is first called on item
349and then on each of its children. Results are concatenated in a list.
350
351INDENT is a number (default 0) representing the indentation of ITEM in
352HIERARCHY. FUNC should take 2 argument: the item and its indentation
353level."
354 (let ((indent (or indent 0)))
355 (cons
356 (funcall func item indent)
357 (seq-mapcat (lambda (child) (hierarchy-map-item func child
358 hierarchy (1+ indent)))
359 (hierarchy-children hierarchy item)))))
360
361(defun hierarchy-map (func hierarchy &optional indent)
362 "Return the result of applying FUNC to each element of HIERARCHY.
363
364This function navigates the tree top-down: FUNCTION is first called on each
365root. To do so, it calls `hierarchy-map-item' on each root
366sequentially. Results are concatenated in a list.
367
368FUNC should take 2 arguments: the item and its indentation level.
369
370INDENT is a number (default 0) representing the indentation of HIERARCHY's
371roots."
372 (let ((indent (or indent 0)))
373 (seq-mapcat (lambda (root) (hierarchy-map-item func root hierarchy indent))
374 (hierarchy-roots hierarchy))))
375
376(defun hierarchy-map-tree (function hierarchy &optional item indent)
377 "Apply FUNCTION on each item of HIERARCHY under ITEM.
378
379This function navigates the tree bottom-up: FUNCTION is first called on
380leafs and the result is passed as parameter when calling FUNCTION on
381parents.
382
383FUNCTION should take 3 parameters: the current item, its indentation
384level (a number), and a list representing the result of applying
385`hierarchy-map-tree' to each child of the item.
386
387INDENT is 0 by default and is passed as second parameter to FUNCTION.
388INDENT is incremented by 1 at each level of the tree.
389
390This function returns the result of applying FUNCTION to ITEM (the first
391root if nil)."
392 (let ((item (or item (car (hierarchy-roots hierarchy))))
393 (indent (or indent 0)))
394 (funcall function item indent
395 (mapcar (lambda (child)
396 (hierarchy-map-tree function hierarchy
397 child (1+ indent)))
398 (hierarchy-children hierarchy item)))))
399
400(defun hierarchy-map-hierarchy (function hierarchy)
401 "Apply FUNCTION to each item of HIERARCHY in a new hierarchy.
402
403FUNCTION should take 2 parameters, the current item and its
404indentation level (a number), and should return an item to be
405added to the new hierarchy."
406 (let* ((items (make-hash-table :test #'equal))
407 (transform (lambda (item) (map-elt items item))))
408 ;; Make 'items', a table mapping original items to their
409 ;; transformation
410 (hierarchy-map (lambda (item indent)
411 (map-put! items item (funcall function item indent)))
412 hierarchy)
413 (hierarchy--make
414 :roots (mapcar transform (hierarchy-roots hierarchy))
415 :parents (let ((result (make-hash-table :test #'equal)))
416 (map-apply (lambda (child parent)
417 (map-put! result
418 (funcall transform child)
419 (funcall transform parent)))
420 (hierarchy--parents hierarchy))
421 result)
422 :children (let ((result (make-hash-table :test #'equal)))
423 (map-apply (lambda (parent children)
424 (map-put! result
425 (funcall transform parent)
426 (seq-map transform children)))
427 (hierarchy--children hierarchy))
428 result)
429 :seen-items (let ((result (make-hash-table :test #'equal)))
430 (map-apply (lambda (item v)
431 (map-put! result
432 (funcall transform item)
433 v))
434 (hierarchy--seen-items hierarchy))
435 result))))
436
437
438;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
439;; Display
440;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
441
442(defun hierarchy-labelfn-indent (labelfn &optional indent-string)
443 "Return a function rendering LABELFN indented with INDENT-STRING.
444
445INDENT-STRING defaults to a 2-space string. Indentation is
446multiplied by the depth of the displayed item."
447 (let ((indent-string (or indent-string " ")))
448 (lambda (item indent)
449 (dotimes (_ indent) (insert indent-string))
450 (funcall labelfn item indent))))
451
452(defun hierarchy-labelfn-button (labelfn actionfn)
453 "Return a function rendering LABELFN in a button.
454
455Clicking the button triggers ACTIONFN. ACTIONFN is a function
456taking an item of HIERARCHY and an indentation value (a number)
457as input. This function is called when an item is clicked. The
458return value of ACTIONFN is ignored."
459 (lambda (item indent)
460 (let ((start (point)))
461 (funcall labelfn item indent)
462 (make-text-button start (point)
463 'action (lambda (_) (funcall actionfn item indent))))))
464
465(defun hierarchy-labelfn-button-if (labelfn buttonp actionfn)
466 "Return a function rendering LABELFN as a button if BUTTONP.
467
468Pass LABELFN and ACTIONFN to `hierarchy-labelfn-button' if
469BUTTONP is non-nil. Otherwise, render LABELFN without making it
470a button.
471
472BUTTONP is a function taking an item of HIERARCHY and an
473indentation value (a number) as input."
474 (lambda (item indent)
475 (if (funcall buttonp item indent)
476 (funcall (hierarchy-labelfn-button labelfn actionfn) item indent)
477 (funcall labelfn item indent))))
478
479(defun hierarchy-labelfn-to-string (labelfn item indent)
480 "Execute LABELFN on ITEM and INDENT. Return result as a string."
481 (with-temp-buffer
482 (funcall labelfn item indent)
483 (buffer-substring (point-min) (point-max))))
484
485(defun hierarchy-print (hierarchy &optional to-string)
486 "Insert HIERARCHY in current buffer as plain text.
487
488Use TO-STRING to convert each element to a string. TO-STRING is
489a function taking an item of HIERARCHY as input and returning a
490string. If nil, TO-STRING defaults to a call to `format' with \"%s\"."
491 (let ((to-string (or to-string (lambda (item) (format "%s" item)))))
492 (hierarchy-map
493 (hierarchy-labelfn-indent (lambda (item _)
494 (insert (funcall to-string item) "\n")))
495 hierarchy)))
496
497(defun hierarchy-to-string (hierarchy &optional to-string)
498 "Return a string representing HIERARCHY.
499
500TO-STRING is passed unchanged to `hierarchy-print'."
501 (with-temp-buffer
502 (hierarchy-print hierarchy to-string)
503 (buffer-substring (point-min) (point-max))))
504
505(defun hierarchy-tabulated-imenu-action (_item-name position)
506 "Move to ITEM-NAME at POSITION in current buffer."
507 (goto-char position)
508 (back-to-indentation))
509
510(define-derived-mode hierarchy-tabulated-mode tabulated-list-mode "Hierarchy tabulated"
511 "Major mode to display a hierarchy as a tabulated list."
512 (setq-local imenu-generic-expression
513 ;; debbugs: 26457 - Cannot pass a function to
514 ;; imenu-generic-expression. Add
515 ;; `hierarchy-tabulated-imenu-action' to the end of the
516 ;; list when bug is fixed
517 '(("Item" "^[[:space:]]+\\(?1:.+\\)$" 1))))
518
519(defun hierarchy-tabulated-display (hierarchy labelfn &optional buffer)
520 "Display HIERARCHY as a tabulated list in `hierarchy-tabulated-mode'.
521
522LABELFN is a function taking an item of HIERARCHY and an indentation
523level (a number) as input and inserting a string to be displayed in the
524table.
525
526The tabulated list is displayed in BUFFER, or a newly created buffer if
527nil. The buffer is returned."
528 (let ((buffer (or buffer (generate-new-buffer "hierarchy-tabulated"))))
529 (with-current-buffer buffer
530 (hierarchy-tabulated-mode)
531 (setq tabulated-list-format
532 (vector '("Item name" 0 nil)))
533 (setq tabulated-list-entries
534 (hierarchy-map (lambda (item indent)
535 (list item (vector (hierarchy-labelfn-to-string
536 labelfn item indent))))
537 hierarchy))
538 (tabulated-list-init-header)
539 (tabulated-list-print))
540 buffer))
541
542(declare-function widget-convert "wid-edit")
543(defun hierarchy-convert-to-tree-widget (hierarchy labelfn)
544 "Return a tree-widget for HIERARCHY.
545
546LABELFN is a function taking an item of HIERARCHY and an indentation
547value (a number) as parameter and inserting a string to be displayed as a
548node label."
549 (require 'wid-edit)
550 (require 'tree-widget)
551 (hierarchy-map-tree (lambda (item indent children)
552 (widget-convert
553 'tree-widget
554 :tag (hierarchy-labelfn-to-string labelfn item indent)
555 :args children))
556 hierarchy))
557
558(defun hierarchy-tree-display (hierarchy labelfn &optional buffer)
559 "Display HIERARCHY as a tree widget in a new buffer.
560
561HIERARCHY and LABELFN are passed unchanged to
562`hierarchy-convert-to-tree-widget'.
563
564The tree widget is displayed in BUFFER, or a newly created buffer if
565nil. The buffer is returned."
566 (let ((buffer (or buffer (generate-new-buffer "*hierarchy-tree*")))
567 (tree-widget (hierarchy-convert-to-tree-widget hierarchy labelfn)))
568 (with-current-buffer buffer
569 (setq-local buffer-read-only t)
570 (let ((inhibit-read-only t))
571 (erase-buffer)
572 (widget-create tree-widget)
573 (goto-char (point-min))
574 (special-mode)))
575 buffer))
576
577(provide 'hierarchy)
578
579;;; hierarchy.el ends here
diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el
index 4c1a1797adc..1cc68e19edd 100644
--- a/lisp/emacs-lisp/seq.el
+++ b/lisp/emacs-lisp/seq.el
@@ -492,6 +492,7 @@ keys. Keys are compared using `equal'."
492SEQUENCE must be a sequence of numbers or markers." 492SEQUENCE must be a sequence of numbers or markers."
493 (apply #'min (seq-into sequence 'list))) 493 (apply #'min (seq-into sequence 'list)))
494 494
495;;;###autoload
495(cl-defgeneric seq-max (sequence) 496(cl-defgeneric seq-max (sequence)
496 "Return the largest element of SEQUENCE. 497 "Return the largest element of SEQUENCE.
497SEQUENCE must be a sequence of numbers or markers." 498SEQUENCE must be a sequence of numbers or markers."
diff --git a/lisp/epa-file.el b/lisp/epa-file.el
index 20043a9eae4..bbd9279a9a8 100644
--- a/lisp/epa-file.el
+++ b/lisp/epa-file.el
@@ -151,17 +151,25 @@ encryption is used."
151 (nth 3 error))) 151 (nth 3 error)))
152 (let ((exists (file-exists-p local-file))) 152 (let ((exists (file-exists-p local-file)))
153 (when exists 153 (when exists
154 ;; Hack to prevent find-file from opening empty buffer 154 (epa-display-error context)
155 ;; when decryption failed (bug#6568). See the place 155 ;; When the .gpg file isn't an encrypted file (e.g.,
156 ;; where `find-file-not-found-functions' are called in 156 ;; it's a keyring.gpg file instead), then gpg will
157 ;; `find-file-noselect-1'. 157 ;; say "Unexpected exit" as the error message. In
158 (setq-local epa-file-error error) 158 ;; that case, just display the bytes.
159 (add-hook 'find-file-not-found-functions 159 (if (equal (caddr error) "Unexpected; Exit")
160 'epa-file--find-file-not-found-function 160 (setq string (with-temp-buffer
161 nil t) 161 (insert-file-contents-literally local-file)
162 (epa-display-error context)) 162 (buffer-string)))
163 (signal (if exists 'file-error 'file-missing) 163 ;; Hack to prevent find-file from opening empty buffer
164 (cons "Opening input file" (cdr error)))))) 164 ;; when decryption failed (bug#6568). See the place
165 ;; where `find-file-not-found-functions' are called in
166 ;; `find-file-noselect-1'.
167 (setq-local epa-file-error error)
168 (add-hook 'find-file-not-found-functions
169 'epa-file--find-file-not-found-function
170 nil t)
171 (signal (if exists 'file-error 'file-missing)
172 (cons "Opening input file" (cdr error))))))))
165 (set-buffer buf) ;In case timer/filter changed/killed it (bug#16029)! 173 (set-buffer buf) ;In case timer/filter changed/killed it (bug#16029)!
166 (setq-local epa-file-encrypt-to 174 (setq-local epa-file-encrypt-to
167 (mapcar #'car (epg-context-result-for 175 (mapcar #'car (epg-context-result-for
diff --git a/lisp/erc/erc-capab.el b/lisp/erc/erc-capab.el
index fc45725f789..4afe6a7614b 100644
--- a/lisp/erc/erc-capab.el
+++ b/lisp/erc/erc-capab.el
@@ -170,11 +170,11 @@ PARSED is an `erc-parsed' response struct."
170 (string-match "^\\([-\\+]\\)\\(.+\\)$" msg)) 170 (string-match "^\\([-\\+]\\)\\(.+\\)$" msg))
171 (setf (erc-response.contents parsed) 171 (setf (erc-response.contents parsed)
172 (if erc-capab-identify-mode 172 (if erc-capab-identify-mode
173 (erc-propertize (match-string 2 msg) 173 (propertize (match-string 2 msg)
174 'erc-identified 174 'erc-identified
175 (if (string= (match-string 1 msg) "+") 175 (if (string= (match-string 1 msg) "+")
176 1 176 1
177 0)) 177 0))
178 (match-string 2 msg))) 178 (match-string 2 msg)))
179 nil))) 179 nil)))
180 180
@@ -190,9 +190,9 @@ PARSED is an `erc-parsed' response struct."
190 ;; assuming the first use of `nickname' is the sender's nick 190 ;; assuming the first use of `nickname' is the sender's nick
191 (re-search-forward (regexp-quote nickname) nil t)) 191 (re-search-forward (regexp-quote nickname) nil t))
192 (goto-char (match-beginning 0)) 192 (goto-char (match-beginning 0))
193 (insert (erc-propertize erc-capab-identify-prefix 193 (insert (propertize erc-capab-identify-prefix
194 'font-lock-face 194 'font-lock-face
195 'erc-capab-identify-unidentified)))))) 195 'erc-capab-identify-unidentified))))))
196 196
197(defun erc-capab-identify-get-unidentified-nickname (parsed) 197(defun erc-capab-identify-get-unidentified-nickname (parsed)
198 "Return the nickname of the user if unidentified. 198 "Return the nickname of the user if unidentified.
diff --git a/lisp/erc/erc-compat.el b/lisp/erc/erc-compat.el
index 388728b04a0..d71221b2674 100644
--- a/lisp/erc/erc-compat.el
+++ b/lisp/erc/erc-compat.el
@@ -43,12 +43,12 @@ Return the same string, if the encoding operation is trivial.
43See `erc-encoding-coding-alist'." 43See `erc-encoding-coding-alist'."
44 (encode-coding-string s coding-system t)) 44 (encode-coding-string s coding-system t))
45 45
46(defalias 'erc-propertize 'propertize) 46(define-obsolete-function-alias 'erc-propertize #'propertize "28.1")
47(defalias 'erc-view-mode-enter 'view-mode-enter) 47(define-obsolete-function-alias 'erc-view-mode-enter #'view-mode-enter "28.1")
48(autoload 'help-function-arglist "help-fns") 48(autoload 'help-function-arglist "help-fns")
49(defalias 'erc-function-arglist 'help-function-arglist) 49(define-obsolete-function-alias 'erc-function-arglist #'help-function-arglist "28.1")
50(defalias 'erc-delete-dups 'delete-dups) 50(define-obsolete-function-alias 'erc-delete-dups #'delete-dups "28.1")
51(defalias 'erc-replace-regexp-in-string 'replace-regexp-in-string) 51(define-obsolete-function-alias 'erc-replace-regexp-in-string #'replace-regexp-in-string "28.1")
52 52
53(defun erc-set-write-file-functions (new-val) 53(defun erc-set-write-file-functions (new-val)
54 (set (make-local-variable 'write-file-functions) new-val)) 54 (set (make-local-variable 'write-file-functions) new-val))
diff --git a/lisp/erc/erc-dcc.el b/lisp/erc/erc-dcc.el
index 8ccceec4594..bf98eb818f3 100644
--- a/lisp/erc/erc-dcc.el
+++ b/lisp/erc/erc-dcc.el
@@ -423,7 +423,7 @@ where FOO is one of CLOSE, GET, SEND, LIST, CHAT, etc."
423 #'(lambda (elt) 423 #'(lambda (elt)
424 (eq (plist-get elt :type) 'CHAT)) 424 (eq (plist-get elt :type) 'CHAT))
425 erc-dcc-list))) 425 erc-dcc-list)))
426 ('close (erc-delete-dups 426 ('close (delete-dups
427 (mapcar (lambda (elt) (symbol-name (plist-get elt :type))) 427 (mapcar (lambda (elt) (symbol-name (plist-get elt :type)))
428 erc-dcc-list))) 428 erc-dcc-list)))
429 ('get (mapcar #'erc-dcc-nick 429 ('get (mapcar #'erc-dcc-nick
@@ -636,8 +636,8 @@ that subcommand."
636 636
637(define-inline erc-dcc-unquote-filename (filename) 637(define-inline erc-dcc-unquote-filename (filename)
638 (inline-quote 638 (inline-quote
639 (erc-replace-regexp-in-string "\\\\\\\\" "\\" 639 (replace-regexp-in-string "\\\\\\\\" "\\"
640 (erc-replace-regexp-in-string "\\\\\"" "\"" ,filename t t) t t))) 640 (replace-regexp-in-string "\\\\\"" "\"" ,filename t t) t t)))
641 641
642(defun erc-dcc-handle-ctcp-send (proc query nick login host to) 642(defun erc-dcc-handle-ctcp-send (proc query nick login host to)
643 "This is called if a CTCP DCC SEND subcommand is sent to the client. 643 "This is called if a CTCP DCC SEND subcommand is sent to the client.
@@ -1193,8 +1193,8 @@ other client."
1193 (setq posn (match-end 0)) 1193 (setq posn (match-end 0))
1194 (erc-display-message 1194 (erc-display-message
1195 nil nil proc 1195 nil nil proc
1196 'dcc-chat-privmsg ?n (erc-propertize erc-dcc-from 'font-lock-face 1196 'dcc-chat-privmsg ?n (propertize erc-dcc-from 'font-lock-face
1197 'erc-nick-default-face) ?m line)) 1197 'erc-nick-default-face) ?m line))
1198 (setq erc-dcc-unprocessed-output (substring str posn))))) 1198 (setq erc-dcc-unprocessed-output (substring str posn)))))
1199 1199
1200(defun erc-dcc-chat-buffer-killed () 1200(defun erc-dcc-chat-buffer-killed ()
diff --git a/lisp/erc/erc-list.el b/lisp/erc/erc-list.el
index 5faeabb721a..036d7733ed7 100644
--- a/lisp/erc/erc-list.el
+++ b/lisp/erc/erc-list.el
@@ -71,13 +71,13 @@
71(defun erc-list-make-string (channel users topic) 71(defun erc-list-make-string (channel users topic)
72 (concat 72 (concat
73 channel 73 channel
74 (erc-propertize " " 74 (propertize " "
75 'display (list 'space :align-to erc-list-nusers-column) 75 'display (list 'space :align-to erc-list-nusers-column)
76 'face 'fixed-pitch) 76 'face 'fixed-pitch)
77 users 77 users
78 (erc-propertize " " 78 (propertize " "
79 'display (list 'space :align-to erc-list-topic-column) 79 'display (list 'space :align-to erc-list-topic-column)
80 'face 'fixed-pitch) 80 'face 'fixed-pitch)
81 topic)) 81 topic))
82 82
83;; Insert a record into the list buffer. 83;; Insert a record into the list buffer.
@@ -143,19 +143,19 @@
143 143
144;; Helper function that makes a buttonized column header. 144;; Helper function that makes a buttonized column header.
145(defun erc-list-button (title column) 145(defun erc-list-button (title column)
146 (erc-propertize title 146 (propertize title
147 'column-number column 147 'column-number column
148 'help-echo "mouse-1: sort by column" 148 'help-echo "mouse-1: sort by column"
149 'mouse-face 'header-line-highlight 149 'mouse-face 'header-line-highlight
150 'keymap erc-list-menu-sort-button-map)) 150 'keymap erc-list-menu-sort-button-map))
151 151
152(define-derived-mode erc-list-menu-mode special-mode "ERC-List" 152(define-derived-mode erc-list-menu-mode special-mode "ERC-List"
153 "Major mode for editing a list of irc channels." 153 "Major mode for editing a list of irc channels."
154 (setq header-line-format 154 (setq header-line-format
155 (concat 155 (concat
156 (erc-propertize " " 156 (propertize " "
157 'display '(space :align-to 0) 157 'display '(space :align-to 0)
158 'face 'fixed-pitch) 158 'face 'fixed-pitch)
159 (erc-list-make-string (erc-list-button "Channel" 1) 159 (erc-list-make-string (erc-list-button "Channel" 1)
160 (erc-list-button "# Users" 2) 160 (erc-list-button "# Users" 2)
161 "Topic"))) 161 "Topic")))
diff --git a/lisp/erc/erc-log.el b/lisp/erc/erc-log.el
index 1bad6d16c87..e2c066da9b1 100644
--- a/lisp/erc/erc-log.el
+++ b/lisp/erc/erc-log.el
@@ -334,7 +334,7 @@ This will not work with full paths, only names.
334 334
335Any unsafe characters in the name are replaced with \"!\". The 335Any unsafe characters in the name are replaced with \"!\". The
336filename is downcased." 336filename is downcased."
337 (downcase (erc-replace-regexp-in-string 337 (downcase (replace-regexp-in-string
338 "[/\\]" "!" (convert-standard-filename filename)))) 338 "[/\\]" "!" (convert-standard-filename filename))))
339 339
340(defun erc-current-logfile (&optional buffer) 340(defun erc-current-logfile (&optional buffer)
diff --git a/lisp/erc/erc-match.el b/lisp/erc/erc-match.el
index 0e98f2bc613..6e87a183fc1 100644
--- a/lisp/erc/erc-match.el
+++ b/lisp/erc/erc-match.el
@@ -577,9 +577,9 @@ See `erc-log-match-format'."
577 (with-current-buffer buffer 577 (with-current-buffer buffer
578 (unless buffer-already 578 (unless buffer-already
579 (insert " == Type \"q\" to dismiss messages ==\n") 579 (insert " == Type \"q\" to dismiss messages ==\n")
580 (erc-view-mode-enter nil (lambda (buffer) 580 (view-mode-enter nil (lambda (buffer)
581 (when (y-or-n-p "Discard messages? ") 581 (when (y-or-n-p "Discard messages? ")
582 (kill-buffer buffer))))) 582 (kill-buffer buffer)))))
583 buffer))) 583 buffer)))
584 584
585(defun erc-log-matches-come-back (proc parsed) 585(defun erc-log-matches-come-back (proc parsed)
diff --git a/lisp/erc/erc-networks.el b/lisp/erc/erc-networks.el
index 415fb53fee0..8551cdd1dee 100644
--- a/lisp/erc/erc-networks.el
+++ b/lisp/erc/erc-networks.el
@@ -812,7 +812,7 @@ As an example:
812 (let* ((completion-ignore-case t) 812 (let* ((completion-ignore-case t)
813 (net (intern 813 (net (intern
814 (completing-read "Network: " 814 (completing-read "Network: "
815 (erc-delete-dups 815 (delete-dups
816 (mapcar (lambda (x) 816 (mapcar (lambda (x)
817 (list (symbol-name (nth 1 x)))) 817 (list (symbol-name (nth 1 x))))
818 erc-server-alist))))) 818 erc-server-alist)))))
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 8830dd4c45e..404a4c09975 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -63,6 +63,8 @@
63(require 'thingatpt) 63(require 'thingatpt)
64(require 'auth-source) 64(require 'auth-source)
65(require 'erc-compat) 65(require 'erc-compat)
66(require 'time-date)
67(require 'iso8601)
66(eval-when-compile (require 'subr-x)) 68(eval-when-compile (require 'subr-x))
67 69
68(defvar erc-official-location 70(defvar erc-official-location
@@ -1628,9 +1630,10 @@ symbol, it may have these values:
1628 (and (erc-server-buffer-p) 1630 (and (erc-server-buffer-p)
1629 (not (erc-server-process-alive))))) 1631 (not (erc-server-process-alive)))))
1630 ;; Channel buffer; check that it's from the right server. 1632 ;; Channel buffer; check that it's from the right server.
1631 (with-current-buffer (get-buffer candidate) 1633 (and target
1632 (and (string= erc-session-server server) 1634 (with-current-buffer (get-buffer candidate)
1633 (erc-port-equal erc-session-port port))))) 1635 (and (string= erc-session-server server)
1636 (erc-port-equal erc-session-port port))))))
1634 (setq buffer-name candidate))) 1637 (setq buffer-name candidate)))
1635 ;; if buffer-name is unset, neither candidate worked out for us, 1638 ;; if buffer-name is unset, neither candidate worked out for us,
1636 ;; fallback to the old <N> uniquification method: 1639 ;; fallback to the old <N> uniquification method:
@@ -1860,7 +1863,7 @@ buffer rather than a server buffer.")
1860 ;; modify `transforms' to specify what needs to be changed 1863 ;; modify `transforms' to specify what needs to be changed
1861 ;; each item is in the format '(old . new) 1864 ;; each item is in the format '(old . new)
1862 (let ((transforms '((pcomplete . completion)))) 1865 (let ((transforms '((pcomplete . completion))))
1863 (erc-delete-dups 1866 (delete-dups
1864 (mapcar (lambda (m) (or (cdr (assoc m transforms)) m)) 1867 (mapcar (lambda (m) (or (cdr (assoc m transforms)) m))
1865 mods)))) 1868 mods))))
1866 1869
@@ -2313,7 +2316,7 @@ and appears in face `erc-input-face' in the buffer."
2313 (setq result (concat result network-name 2316 (setq result (concat result network-name
2314 " << " line "\n"))) 2317 " << " line "\n")))
2315 result) 2318 result)
2316 (erc-propertize 2319 (propertize
2317 (concat network-name " >> " string 2320 (concat network-name " >> " string
2318 (if (/= ?\n 2321 (if (/= ?\n
2319 (aref string 2322 (aref string
@@ -2336,7 +2339,7 @@ If ARG is non-nil, show the *erc-protocol* buffer."
2336 (interactive "P") 2339 (interactive "P")
2337 (let* ((buf (get-buffer-create "*erc-protocol*"))) 2340 (let* ((buf (get-buffer-create "*erc-protocol*")))
2338 (with-current-buffer buf 2341 (with-current-buffer buf
2339 (erc-view-mode-enter) 2342 (view-mode-enter)
2340 (when (null (current-local-map)) 2343 (when (null (current-local-map))
2341 (let ((inhibit-read-only t)) 2344 (let ((inhibit-read-only t))
2342 (insert (erc-make-notice "This buffer displays all IRC protocol traffic exchanged with each server.\n")) 2345 (insert (erc-make-notice "This buffer displays all IRC protocol traffic exchanged with each server.\n"))
@@ -2770,7 +2773,7 @@ See also `erc-server-send'."
2770 2773
2771(defun erc-get-arglist (fun) 2774(defun erc-get-arglist (fun)
2772 "Return the argument list of a function without the parens." 2775 "Return the argument list of a function without the parens."
2773 (let ((arglist (format "%S" (erc-function-arglist fun)))) 2776 (let ((arglist (format "%S" (help-function-arglist fun))))
2774 (if (string-match "\\`(\\(.*\\))\\'" arglist) 2777 (if (string-match "\\`(\\(.*\\))\\'" arglist)
2775 (match-string 1 arglist) 2778 (match-string 1 arglist)
2776 arglist))) 2779 arglist)))
@@ -2905,6 +2908,44 @@ therefore has to contain the command itself as well."
2905 (erc-server-send (substring line 1)) 2908 (erc-server-send (substring line 1))
2906 t) 2909 t)
2907 2910
2911(defvar erc--read-time-period-history nil)
2912
2913(defun erc--read-time-period (prompt)
2914 "Read a time period on the \"2h\" format.
2915If there's no letter spec, the input is interpreted as a number of seconds.
2916
2917If input is blank, this function returns nil. Otherwise it
2918returns the time spec converted to a number of seconds."
2919 (let ((period (string-trim
2920 (read-string prompt nil 'erc--read-time-period-history))))
2921 (cond
2922 ;; Blank input.
2923 ((zerop (length period))
2924 nil)
2925 ;; All-number -- interpret as seconds.
2926 ((string-match-p "\\`[0-9]+\\'" period)
2927 (string-to-number period))
2928 ;; Parse as a time spec.
2929 (t
2930 (let ((time (condition-case nil
2931 (iso8601-parse-duration
2932 (concat (cond
2933 ((string-match-p "\\`P" (upcase period))
2934 ;; Somebody typed in a full ISO8601 period.
2935 (upcase period))
2936 ((string-match-p "[YD]" (upcase period))
2937 ;; If we have a year/day element,
2938 ;; we have a full spec.
2939 "P")
2940 (t
2941 ;; Otherwise it's just a sub-day spec.
2942 "PT"))
2943 (upcase period)))
2944 (wrong-type-argument nil))))
2945 (unless time
2946 (user-error "%s is not a valid time period" period))
2947 (decoded-time-period time))))))
2948
2908(defun erc-cmd-IGNORE (&optional user) 2949(defun erc-cmd-IGNORE (&optional user)
2909 "Ignore USER. This should be a regexp matching nick!user@host. 2950 "Ignore USER. This should be a regexp matching nick!user@host.
2910If no USER argument is specified, list the contents of `erc-ignore-list'." 2951If no USER argument is specified, list the contents of `erc-ignore-list'."
@@ -2914,10 +2955,18 @@ If no USER argument is specified, list the contents of `erc-ignore-list'."
2914 (y-or-n-p (format "Use regexp-quoted form (%s) instead? " 2955 (y-or-n-p (format "Use regexp-quoted form (%s) instead? "
2915 quoted))) 2956 quoted)))
2916 (setq user quoted)) 2957 (setq user quoted))
2917 (erc-display-line 2958 (let ((timeout
2918 (erc-make-notice (format "Now ignoring %s" user)) 2959 (erc--read-time-period
2919 'active) 2960 "Add a timeout? (Blank for no, or a time spec like 2h): "))
2920 (erc-with-server-buffer (add-to-list 'erc-ignore-list user))) 2961 (buffer (current-buffer)))
2962 (when timeout
2963 (run-at-time timeout nil
2964 (lambda ()
2965 (erc--unignore-user user buffer))))
2966 (erc-display-line
2967 (erc-make-notice (format "Now ignoring %s" user))
2968 'active)
2969 (erc-with-server-buffer (add-to-list 'erc-ignore-list user))))
2921 (if (null (erc-with-server-buffer erc-ignore-list)) 2970 (if (null (erc-with-server-buffer erc-ignore-list))
2922 (erc-display-line (erc-make-notice "Ignore list is empty") 'active) 2971 (erc-display-line (erc-make-notice "Ignore list is empty") 'active)
2923 (erc-display-line (erc-make-notice "Ignore list:") 'active) 2972 (erc-display-line (erc-make-notice "Ignore list:") 'active)
@@ -2941,12 +2990,17 @@ If no USER argument is specified, list the contents of `erc-ignore-list'."
2941 (erc-make-notice (format "%s is not currently ignored!" user)) 2990 (erc-make-notice (format "%s is not currently ignored!" user))
2942 'active))) 2991 'active)))
2943 (when ignored-nick 2992 (when ignored-nick
2993 (erc--unignore-user user (current-buffer))))
2994 t)
2995
2996(defun erc--unignore-user (user buffer)
2997 (when (buffer-live-p buffer)
2998 (with-current-buffer buffer
2944 (erc-display-line 2999 (erc-display-line
2945 (erc-make-notice (format "No longer ignoring %s" user)) 3000 (erc-make-notice (format "No longer ignoring %s" user))
2946 'active) 3001 'active)
2947 (erc-with-server-buffer 3002 (erc-with-server-buffer
2948 (setq erc-ignore-list (delete ignored-nick erc-ignore-list))))) 3003 (setq erc-ignore-list (delete user erc-ignore-list))))))
2949 t)
2950 3004
2951(defun erc-cmd-CLEAR () 3005(defun erc-cmd-CLEAR ()
2952 "Clear the window content." 3006 "Clear the window content."
@@ -3504,7 +3558,7 @@ If S is non-nil, it will be used as the quit reason."
3504If S is non-nil, it will be used as the quit reason." 3558If S is non-nil, it will be used as the quit reason."
3505 (or s 3559 (or s
3506 (if (fboundp 'yow) 3560 (if (fboundp 'yow)
3507 (erc-replace-regexp-in-string "\n" "" (yow)) 3561 (replace-regexp-in-string "\n" "" (yow))
3508 (erc-quit/part-reason-default)))) 3562 (erc-quit/part-reason-default))))
3509 3563
3510(make-obsolete 'erc-quit-reason-zippy "it will be removed." "24.4") 3564(make-obsolete 'erc-quit-reason-zippy "it will be removed." "24.4")
@@ -3531,7 +3585,7 @@ If S is non-nil, it will be used as the part reason."
3531If S is non-nil, it will be used as the quit reason." 3585If S is non-nil, it will be used as the quit reason."
3532 (or s 3586 (or s
3533 (if (fboundp 'yow) 3587 (if (fboundp 'yow)
3534 (erc-replace-regexp-in-string "\n" "" (yow)) 3588 (replace-regexp-in-string "\n" "" (yow))
3535 (erc-quit/part-reason-default)))) 3589 (erc-quit/part-reason-default))))
3536 3590
3537(make-obsolete 'erc-part-reason-zippy "it will be removed." "24.4") 3591(make-obsolete 'erc-part-reason-zippy "it will be removed." "24.4")
@@ -3947,13 +4001,13 @@ If FACE is non-nil, it will be used to propertize the prompt. If it is nil,
3947 ;; Do not extend the text properties when typing at the end 4001 ;; Do not extend the text properties when typing at the end
3948 ;; of the prompt, but stuff typed in front of the prompt 4002 ;; of the prompt, but stuff typed in front of the prompt
3949 ;; shall remain part of the prompt. 4003 ;; shall remain part of the prompt.
3950 (setq prompt (erc-propertize prompt 4004 (setq prompt (propertize prompt
3951 'start-open t ; XEmacs 4005 'start-open t ; XEmacs
3952 'rear-nonsticky t ; Emacs 4006 'rear-nonsticky t ; Emacs
3953 'erc-prompt t 4007 'erc-prompt t
3954 'field t 4008 'field t
3955 'front-sticky t 4009 'front-sticky t
3956 'read-only t)) 4010 'read-only t))
3957 (erc-put-text-property 0 (1- (length prompt)) 4011 (erc-put-text-property 0 (1- (length prompt))
3958 'font-lock-face (or face 'erc-prompt-face) 4012 'font-lock-face (or face 'erc-prompt-face)
3959 prompt) 4013 prompt)
@@ -4336,15 +4390,15 @@ See also `erc-format-nick-function'."
4336(defun erc-get-user-mode-prefix (user) 4390(defun erc-get-user-mode-prefix (user)
4337 (when user 4391 (when user
4338 (cond ((erc-channel-user-owner-p user) 4392 (cond ((erc-channel-user-owner-p user)
4339 (erc-propertize "~" 'help-echo "owner")) 4393 (propertize "~" 'help-echo "owner"))
4340 ((erc-channel-user-admin-p user) 4394 ((erc-channel-user-admin-p user)
4341 (erc-propertize "&" 'help-echo "admin")) 4395 (propertize "&" 'help-echo "admin"))
4342 ((erc-channel-user-op-p user) 4396 ((erc-channel-user-op-p user)
4343 (erc-propertize "@" 'help-echo "operator")) 4397 (propertize "@" 'help-echo "operator"))
4344 ((erc-channel-user-halfop-p user) 4398 ((erc-channel-user-halfop-p user)
4345 (erc-propertize "%" 'help-echo "half-op")) 4399 (propertize "%" 'help-echo "half-op"))
4346 ((erc-channel-user-voice-p user) 4400 ((erc-channel-user-voice-p user)
4347 (erc-propertize "+" 'help-echo "voice")) 4401 (propertize "+" 'help-echo "voice"))
4348 (t "")))) 4402 (t ""))))
4349 4403
4350(defun erc-format-@nick (&optional user _channel-data) 4404(defun erc-format-@nick (&optional user _channel-data)
@@ -4355,7 +4409,7 @@ prefix. Use CHANNEL-DATA to determine op and voice status. See
4355also `erc-format-nick-function'." 4409also `erc-format-nick-function'."
4356 (when user 4410 (when user
4357 (let ((nick (erc-server-user-nickname user))) 4411 (let ((nick (erc-server-user-nickname user)))
4358 (concat (erc-propertize 4412 (concat (propertize
4359 (erc-get-user-mode-prefix nick) 4413 (erc-get-user-mode-prefix nick)
4360 'font-lock-face 'erc-nick-prefix-face) 4414 'font-lock-face 'erc-nick-prefix-face)
4361 nick)))) 4415 nick))))
@@ -4368,12 +4422,12 @@ also `erc-format-nick-function'."
4368 (nick (erc-current-nick)) 4422 (nick (erc-current-nick))
4369 (mode (erc-get-user-mode-prefix nick))) 4423 (mode (erc-get-user-mode-prefix nick)))
4370 (concat 4424 (concat
4371 (erc-propertize open 'font-lock-face 'erc-default-face) 4425 (propertize open 'font-lock-face 'erc-default-face)
4372 (erc-propertize mode 'font-lock-face 'erc-my-nick-prefix-face) 4426 (propertize mode 'font-lock-face 'erc-my-nick-prefix-face)
4373 (erc-propertize nick 'font-lock-face 'erc-my-nick-face) 4427 (propertize nick 'font-lock-face 'erc-my-nick-face)
4374 (erc-propertize close 'font-lock-face 'erc-default-face))) 4428 (propertize close 'font-lock-face 'erc-default-face)))
4375 (let ((prefix "> ")) 4429 (let ((prefix "> "))
4376 (erc-propertize prefix 'font-lock-face 'erc-default-face)))) 4430 (propertize prefix 'font-lock-face 'erc-default-face))))
4377 4431
4378(defun erc-echo-notice-in-default-buffer (s parsed buffer _sender) 4432(defun erc-echo-notice-in-default-buffer (s parsed buffer _sender)
4379 "Echos a private notice in the default buffer, namely the 4433 "Echos a private notice in the default buffer, namely the
@@ -6435,16 +6489,16 @@ if `erc-away' is non-nil."
6435 (fill-region (point-min) (point-max)) 6489 (fill-region (point-min) (point-max))
6436 (buffer-string)))) 6490 (buffer-string))))
6437 (setq header-line-format 6491 (setq header-line-format
6438 (erc-replace-regexp-in-string 6492 (replace-regexp-in-string
6439 "%" 6493 "%"
6440 "%%" 6494 "%%"
6441 (if face 6495 (if face
6442 (erc-propertize header 'help-echo help-echo 6496 (propertize header 'help-echo help-echo
6443 'face face) 6497 'face face)
6444 (erc-propertize header 'help-echo help-echo)))))) 6498 (propertize header 'help-echo help-echo))))))
6445 (t (setq header-line-format 6499 (t (setq header-line-format
6446 (if face 6500 (if face
6447 (erc-propertize header 'face face) 6501 (propertize header 'face face)
6448 header))))))) 6502 header)))))))
6449 (force-mode-line-update))) 6503 (force-mode-line-update)))
6450 6504
@@ -6711,7 +6765,7 @@ functions."
6711 nick user host channel 6765 nick user host channel
6712 (if (not (string= reason "")) 6766 (if (not (string= reason ""))
6713 (format ": %s" 6767 (format ": %s"
6714 (erc-replace-regexp-in-string "%" "%%" reason)) 6768 (replace-regexp-in-string "%" "%%" reason))
6715 ""))))) 6769 "")))))
6716 6770
6717 6771
diff --git a/lisp/files.el b/lisp/files.el
index 742fd78df1d..19096693461 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -2683,8 +2683,6 @@ since only a single case-insensitive search through the alist is made."
2683 ("\\.p\\'" . pascal-mode) 2683 ("\\.p\\'" . pascal-mode)
2684 ("\\.pas\\'" . pascal-mode) 2684 ("\\.pas\\'" . pascal-mode)
2685 ("\\.\\(dpr\\|DPR\\)\\'" . delphi-mode) 2685 ("\\.\\(dpr\\|DPR\\)\\'" . delphi-mode)
2686 ("\\.ad[abs]\\'" . ada-mode)
2687 ("\\.ad[bs]\\.dg\\'" . ada-mode)
2688 ("\\.\\([pP]\\([Llm]\\|erl\\|od\\)\\|al\\)\\'" . perl-mode) 2686 ("\\.\\([pP]\\([Llm]\\|erl\\|od\\)\\|al\\)\\'" . perl-mode)
2689 ("Imakefile\\'" . makefile-imake-mode) 2687 ("Imakefile\\'" . makefile-imake-mode)
2690 ("Makeppfile\\(?:\\.mk\\)?\\'" . makefile-makepp-mode) ; Put this before .mk 2688 ("Makeppfile\\(?:\\.mk\\)?\\'" . makefile-makepp-mode) ; Put this before .mk
diff --git a/lisp/finder.el b/lisp/finder.el
index f04d73e098f..820d6d0a3b9 100644
--- a/lisp/finder.el
+++ b/lisp/finder.el
@@ -197,7 +197,7 @@ from; the default is `load-path'."
197 (cons d f)) 197 (cons d f))
198 (directory-files d nil el-file-regexp)))) 198 (directory-files d nil el-file-regexp))))
199 (progress (make-progress-reporter 199 (progress (make-progress-reporter
200 (byte-compile-info-string "Scanning files for finder") 200 (byte-compile-info "Scanning files for finder")
201 0 (length files))) 201 0 (length files)))
202 package-override base-name ; processed 202 package-override base-name ; processed
203 summary keywords package version entry desc) 203 summary keywords package version entry desc)
diff --git a/lisp/generic-x.el b/lisp/generic-x.el
index cd24f497c96..48ac1232051 100644
--- a/lisp/generic-x.el
+++ b/lisp/generic-x.el
@@ -643,7 +643,7 @@ like an INI file. You can add this hook to `find-file-hook'."
643 ("\\([^ =\n\r]+\\)=\\([^ \n\r]*\\)" 643 ("\\([^ =\n\r]+\\)=\\([^ \n\r]*\\)"
644 (1 font-lock-variable-name-face) 644 (1 font-lock-variable-name-face)
645 (2 font-lock-keyword-face))) 645 (2 font-lock-keyword-face)))
646 '("inventory") 646 '("inventory\\'")
647 (list 647 (list
648 (function 648 (function
649 (lambda () 649 (lambda ()
diff --git a/lisp/gnus/gnus-art.el b/lisp/gnus/gnus-art.el
index cb20d7102bd..e0339cc1f32 100644
--- a/lisp/gnus/gnus-art.el
+++ b/lisp/gnus/gnus-art.el
@@ -5849,7 +5849,10 @@ all parts."
5849 (concat "; " gnus-tmp-name)))) 5849 (concat "; " gnus-tmp-name))))
5850 (unless (equal gnus-tmp-description "") 5850 (unless (equal gnus-tmp-description "")
5851 (setq gnus-tmp-type-long (concat " --- " gnus-tmp-type-long))) 5851 (setq gnus-tmp-type-long (concat " --- " gnus-tmp-type-long)))
5852 (when (zerop gnus-tmp-length) 5852 (when (and (zerop gnus-tmp-length)
5853 ;; Only nnimap supports partial fetches so far.
5854 nnimap-fetch-partial-articles
5855 (string-match "^nnimap\\+" gnus-newsgroup-name))
5853 (setq gnus-tmp-type-long 5856 (setq gnus-tmp-type-long
5854 (concat 5857 (concat
5855 gnus-tmp-type-long 5858 gnus-tmp-type-long
@@ -6018,6 +6021,7 @@ If nil, don't show those extra buttons."
6018(defun gnus-mime-display-single (handle) 6021(defun gnus-mime-display-single (handle)
6019 (let ((type (mm-handle-media-type handle)) 6022 (let ((type (mm-handle-media-type handle))
6020 (ignored gnus-ignored-mime-types) 6023 (ignored gnus-ignored-mime-types)
6024 (mm-inline-font-lock (gnus-visual-p 'article-highlight 'highlight))
6021 (not-attachment t) 6025 (not-attachment t)
6022 display text) 6026 display text)
6023 (catch 'ignored 6027 (catch 'ignored
@@ -8340,6 +8344,7 @@ url is put as the `gnus-button-url' overlay property on the button."
8340 (and (match-end 6) (list (string-to-number (match-string 6 address)))))))) 8344 (and (match-end 6) (list (string-to-number (match-string 6 address))))))))
8341 8345
8342(defun gnus-url-parse-query-string (query &optional downcase) 8346(defun gnus-url-parse-query-string (query &optional downcase)
8347 (declare (obsolete message-parse-mailto-url "28.1"))
8343 (let (retval pairs cur key val) 8348 (let (retval pairs cur key val)
8344 (setq pairs (split-string query "&")) 8349 (setq pairs (split-string query "&"))
8345 (while pairs 8350 (while pairs
@@ -8359,31 +8364,8 @@ url is put as the `gnus-button-url' overlay property on the button."
8359 8364
8360(defun gnus-url-mailto (url) 8365(defun gnus-url-mailto (url)
8361 ;; Send mail to someone 8366 ;; Send mail to someone
8362 (setq url (replace-regexp-in-string "\n" " " url)) 8367 (gnus-msg-mail)
8363 (when (string-match "mailto:/*\\(.*\\)" url) 8368 (message-mailto-1 url))
8364 (setq url (substring url (match-beginning 1) nil)))
8365 (let* ((args (gnus-url-parse-query-string
8366 (if (string-match "^\\?" url)
8367 (substring url 1)
8368 (if (string-match "^\\([^?]+\\)\\?\\(.*\\)" url)
8369 (concat "to=" (match-string 1 url) "&"
8370 (match-string 2 url))
8371 (concat "to=" url)))))
8372 (subject (cdr-safe (assoc "subject" args)))
8373 func)
8374 (gnus-msg-mail)
8375 (while args
8376 (setq func (intern-soft (concat "message-goto-" (downcase (caar args)))))
8377 (if (fboundp func)
8378 (funcall func)
8379 (message-position-on-field (caar args)))
8380 (insert (replace-regexp-in-string
8381 "\r\n" "\n"
8382 (mapconcat #'identity (reverse (cdar args)) ", ") nil t))
8383 (setq args (cdr args)))
8384 (if subject
8385 (message-goto-body)
8386 (message-goto-subject))))
8387 8369
8388(defun gnus-button-embedded-url (address) 8370(defun gnus-button-embedded-url (address)
8389 "Activate ADDRESS with `browse-url'." 8371 "Activate ADDRESS with `browse-url'."
diff --git a/lisp/gnus/gnus-icalendar.el b/lisp/gnus/gnus-icalendar.el
index 305e17fd8fc..29d3e30780f 100644
--- a/lisp/gnus/gnus-icalendar.el
+++ b/lisp/gnus/gnus-icalendar.el
@@ -312,7 +312,8 @@ status will be retrieved from the first matching attendee record."
312 312
313 (unless (gnus-icalendar-find-if (lambda (x) (string-match "^ATTENDEE" x)) 313 (unless (gnus-icalendar-find-if (lambda (x) (string-match "^ATTENDEE" x))
314 reply-event-lines) 314 reply-event-lines)
315 (error "Could not find an event attendee matching given identity")) 315 (lwarn 'gnus-icalendar :warning
316 "Could not find an event attendee matching given identity"))
316 317
317 (mapconcat #'identity `("BEGIN:VEVENT" 318 (mapconcat #'identity `("BEGIN:VEVENT"
318 ,@(nreverse reply-event-lines) 319 ,@(nreverse reply-event-lines)
diff --git a/lisp/gnus/gnus-sum.el b/lisp/gnus/gnus-sum.el
index 719498a0337..4363860eac8 100644
--- a/lisp/gnus/gnus-sum.el
+++ b/lisp/gnus/gnus-sum.el
@@ -12284,7 +12284,7 @@ no matter what the properties `:decode' and `:headers' are."
12284 (interactive (gnus-interactive "P\ny")) 12284 (interactive (gnus-interactive "P\ny"))
12285 (require 'gnus-art) 12285 (require 'gnus-art)
12286 (let* ((articles (gnus-summary-work-articles n)) 12286 (let* ((articles (gnus-summary-work-articles n))
12287 (result-buffer "*Shell Command Output*") 12287 (result-buffer shell-command-buffer-name)
12288 (all-headers (not (memq sym '(nil r)))) 12288 (all-headers (not (memq sym '(nil r))))
12289 (gnus-save-all-headers (or all-headers gnus-save-all-headers)) 12289 (gnus-save-all-headers (or all-headers gnus-save-all-headers))
12290 (raw (eq sym 'r)) 12290 (raw (eq sym 'r))
diff --git a/lisp/gnus/gnus-util.el b/lisp/gnus/gnus-util.el
index 8d8956f1fb9..abe546b8cb6 100644
--- a/lisp/gnus/gnus-util.el
+++ b/lisp/gnus/gnus-util.el
@@ -1654,6 +1654,7 @@ The first found will be returned if a file has hard or symbolic links."
1654 "To each element of LIST apply PREDICATE. 1654 "To each element of LIST apply PREDICATE.
1655Return nil if LIST is no list or is empty or some test returns nil; 1655Return nil if LIST is no list or is empty or some test returns nil;
1656otherwise, return t." 1656otherwise, return t."
1657 (declare (obsolete nil "28.1"))
1657 (when (and list (listp list)) 1658 (when (and list (listp list))
1658 (let ((result (mapcar predicate list))) 1659 (let ((result (mapcar predicate list)))
1659 (not (memq nil result))))) 1660 (not (memq nil result)))))
diff --git a/lisp/gnus/gnus-win.el b/lisp/gnus/gnus-win.el
index 36b28350362..baa3146e64e 100644
--- a/lisp/gnus/gnus-win.el
+++ b/lisp/gnus/gnus-win.el
@@ -142,7 +142,7 @@ used to display Gnus windows."
142 (pipe 142 (pipe
143 (vertical 1.0 143 (vertical 1.0
144 (summary 0.25 point) 144 (summary 0.25 point)
145 ("*Shell Command Output*" 1.0))) 145 (shell-command-buffer-name 1.0)))
146 (bug 146 (bug
147 (vertical 1.0 147 (vertical 1.0
148 (if gnus-bug-create-help-buffer '("*Gnus Help Bug*" 0.5)) 148 (if gnus-bug-create-help-buffer '("*Gnus Help Bug*" 0.5))
diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el
index fb560f0eab8..ab625be9e37 100644
--- a/lisp/gnus/message.el
+++ b/lisp/gnus/message.el
@@ -303,6 +303,13 @@ any confusion."
303 :link '(custom-manual "(message)Message Headers") 303 :link '(custom-manual "(message)Message Headers")
304 :type 'regexp) 304 :type 'regexp)
305 305
306(defcustom message-screenshot-command '("import" "png:-")
307 "Command to take a screenshot.
308The command should insert a PNG in the current buffer."
309 :group 'message-various
310 :type '(list string)
311 :version "28.1")
312
306;;; Start of variables adopted from `message-utils.el'. 313;;; Start of variables adopted from `message-utils.el'.
307 314
308(defcustom message-subject-trailing-was-query t 315(defcustom message-subject-trailing-was-query t
@@ -2730,6 +2737,64 @@ systematically send encrypted emails when possible."
2730 (when (message-all-epg-keys-available-p) 2737 (when (message-all-epg-keys-available-p)
2731 (mml-secure-message-sign-encrypt))) 2738 (mml-secure-message-sign-encrypt)))
2732 2739
2740(defcustom message-openpgp-header nil
2741 "Specification for the \"OpenPGP\" header of outgoing messages.
2742
2743The value must be a list of three elements, all strings:
2744- Key ID, in hexadecimal form;
2745- Key URL or ASCII armoured key; and
2746- Protection preference, one of: \"unprotected\", \"sign\",
2747 \"encrypt\" or \"signencrypt\".
2748
2749Each of the elements may be nil, in which case its part in the
2750OpenPGP header will be left out. If all the values are nil,
2751or `message-openpgp-header' is itself nil, the OpenPGP header
2752will not be inserted."
2753 :type '(choice
2754 (const nil :tag "Don't add OpenPGP header")
2755 (list (choice (string :tag "ID")
2756 (const nil :tag "No ID"))
2757 (choice (string :tag "Key")
2758 (const nil :tag "No Key"))
2759 (choice (other nil :tag "None")
2760 (const "unprotected" :tag "Unprotected")
2761 (const "sign" :tag "Sign")
2762 (const "encrypt" :tag "Encrypt")
2763 (const "signencrypt" :tag "Sign and Encrypt"))))
2764 :version "28.1")
2765
2766(defun message-add-openpgp-header ()
2767 "Add OpenPGP header to point to public key.
2768
2769Header will be constructed as specified in `message-openpgp-header'.
2770
2771Consider adding this function to `message-send-hook'."
2772 ;; See https://tools.ietf.org/html/draft-josefsson-openpgp-mailnews-header
2773 (when (and message-openpgp-header
2774 (or (nth 0 message-openpgp-header)
2775 (nth 1 message-openpgp-header)
2776 (nth 2 message-openpgp-header)))
2777 (with-temp-buffer
2778 (insert "OpenPGP: ")
2779 ;; add ID
2780 (let (need-sep)
2781 (when (nth 0 message-openpgp-header)
2782 (insert "id=" (nth 0 message-openpgp-header))
2783 (setq need-sep t))
2784 ;; add URL
2785 (when (nth 1 message-openpgp-header)
2786 (when need-sep (insert "; "))
2787 (if (string-match-p ";")
2788 (insert "url=\"" (nth 1 message-openpgp-header) "\"")
2789 (insert "url=\"" (nth 1 message-openpgp-header) "\""))
2790 (setq need-sep t))
2791 ;; add preference
2792 (when (nth 2 message-openpgp-header)
2793 (when need-sep (insert "; "))
2794 (insert "preference=" (nth 2 message-openpgp-header))))
2795 ;; insert header
2796 (message-add-header (buffer-string)))))
2797
2733 2798
2734 2799
2735;;; 2800;;;
@@ -2810,6 +2875,7 @@ systematically send encrypted emails when possible."
2810 (define-key message-mode-map [remap split-line] 'message-split-line) 2875 (define-key message-mode-map [remap split-line] 'message-split-line)
2811 2876
2812 (define-key message-mode-map "\C-c\C-a" 'mml-attach-file) 2877 (define-key message-mode-map "\C-c\C-a" 'mml-attach-file)
2878 (define-key message-mode-map "\C-c\C-p" 'message-insert-screenshot)
2813 2879
2814 (define-key message-mode-map "\C-a" 'message-beginning-of-line) 2880 (define-key message-mode-map "\C-a" 'message-beginning-of-line)
2815 (define-key message-mode-map "\t" 'message-tab) 2881 (define-key message-mode-map "\t" 'message-tab)
@@ -2839,6 +2905,8 @@ systematically send encrypted emails when possible."
2839 :active (message-mark-active-p) :help "Mark region with enclosing tags"] 2905 :active (message-mark-active-p) :help "Mark region with enclosing tags"]
2840 ["Insert File Marked..." message-mark-insert-file 2906 ["Insert File Marked..." message-mark-insert-file
2841 :help "Insert file at point marked with enclosing tags"] 2907 :help "Insert file at point marked with enclosing tags"]
2908 ["Attach File..." mml-attach-file t]
2909 ["Insert Screenshot" message-insert-screenshot t]
2842 "----" 2910 "----"
2843 ["Send Message" message-send-and-exit :help "Send this message"] 2911 ["Send Message" message-send-and-exit :help "Send this message"]
2844 ["Postpone Message" message-dont-send 2912 ["Postpone Message" message-dont-send
@@ -6988,15 +7056,28 @@ want to get rid of this query permanently.")))
6988 7056
6989 ;; Build the header alist. Allow the user to be asked whether 7057 ;; Build the header alist. Allow the user to be asked whether
6990 ;; or not to reply to all recipients in a wide reply. 7058 ;; or not to reply to all recipients in a wide reply.
6991 (setq follow-to (list (cons 'To (cdr (pop recipients))))) 7059 (when (or (< (length recipients) 2)
6992 (when (and recipients 7060 (not message-wide-reply-confirm-recipients)
6993 (or (not message-wide-reply-confirm-recipients) 7061 (y-or-n-p "Reply to all recipients? "))
6994 (y-or-n-p "Reply to all recipients? "))) 7062 (if never-mct
6995 (setq recipients (mapconcat 7063 ;; The author has requested never to get a (wide)
6996 (lambda (addr) (cdr addr)) recipients ", ")) 7064 ;; response, so put everybody else into the To header.
6997 (if (string-match "^ +" recipients) 7065 ;; This avoids looking as if we're To-in somebody else in
6998 (setq recipients (substring recipients (match-end 0)))) 7066 ;; specific, and just Cc-in the rest.
6999 (push (cons 'Cc recipients) follow-to))) 7067 (setq follow-to (list
7068 (cons 'To
7069 (mapconcat
7070 (lambda (addr)
7071 (cdr addr)) recipients ", "))))
7072 ;; Put the first recipient in the To header.
7073 (setq follow-to (list (cons 'To (cdr (pop recipients)))))
7074 ;; Put the rest of the recipients in Cc.
7075 (when recipients
7076 (setq recipients (mapconcat
7077 (lambda (addr) (cdr addr)) recipients ", "))
7078 (if (string-match "^ +" recipients)
7079 (setq recipients (substring recipients (match-end 0))))
7080 (push (cons 'Cc recipients) follow-to)))))
7000 follow-to)) 7081 follow-to))
7001 7082
7002(defun message-prune-recipients (recipients) 7083(defun message-prune-recipients (recipients)
@@ -8652,6 +8733,108 @@ Used in `message-simplify-recipients'."
8652 (* 0.5 (- (nth 3 edges) (nth 1 edges))))) 8733 (* 0.5 (- (nth 3 edges) (nth 1 edges)))))
8653 string))))))) 8734 string)))))))
8654 8735
8736(defun message-insert-screenshot (delay)
8737 "Take a screenshot and insert in the current buffer.
8738DELAY (the numeric prefix) says how many seconds to wait before
8739starting the screenshotting process.
8740
8741The `message-screenshot-command' variable says what command is
8742used to take the screenshot."
8743 (interactive "p")
8744 (unless (executable-find (car message-screenshot-command))
8745 (error "Can't find %s to take the screenshot"
8746 (car message-screenshot-command)))
8747 (cl-decf delay)
8748 (unless (zerop delay)
8749 (dotimes (i delay)
8750 (message "Sleeping %d second%s..."
8751 (- delay i)
8752 (if (= (- delay i) 1)
8753 ""
8754 "s"))
8755 (sleep-for 1)))
8756 (message "Take screenshot")
8757 (let ((image
8758 (with-temp-buffer
8759 (set-buffer-multibyte nil)
8760 (apply #'call-process
8761 (car message-screenshot-command) nil (current-buffer) nil
8762 (cdr message-screenshot-command))
8763 (buffer-string))))
8764 (set-mark (point))
8765 (insert-image
8766 (create-image image 'png t
8767 :max-width (truncate (* (frame-pixel-width) 0.8))
8768 :max-height (truncate (* (frame-pixel-height) 0.8))
8769 :scale 1)
8770 (format "<#part type=\"image/png\" disposition=inline data-encoding=base64 raw=t>\n%s\n<#/part>"
8771 ;; Get a base64 version of the image -- this avoids later
8772 ;; complications if we're auto-saving the buffer and
8773 ;; restoring from a file.
8774 (with-temp-buffer
8775 (set-buffer-multibyte nil)
8776 (insert image)
8777 (base64-encode-region (point-min) (point-max) t)
8778 (buffer-string))))
8779 (insert "\n\n")
8780 (message "")))
8781
8782(declare-function gnus-url-unhex-string "gnus-util")
8783
8784(defun message-parse-mailto-url (url)
8785 "Parse a mailto: url."
8786 (setq url (replace-regexp-in-string "\n" " " url))
8787 (when (string-match "mailto:/*\\(.*\\)" url)
8788 (setq url (substring url (match-beginning 1) nil)))
8789 (setq url (if (string-match "^\\?" url)
8790 (substring url 1)
8791 (if (string-match "^\\([^?]+\\)\\?\\(.*\\)" url)
8792 (concat "to=" (match-string 1 url) "&"
8793 (match-string 2 url))
8794 (concat "to=" url))))
8795 (let (retval pairs cur key val)
8796 (setq pairs (split-string url "&"))
8797 (while pairs
8798 (setq cur (car pairs)
8799 pairs (cdr pairs))
8800 (if (not (string-match "=" cur))
8801 nil ; Grace
8802 (setq key (downcase (gnus-url-unhex-string
8803 (substring cur 0 (match-beginning 0))))
8804 val (gnus-url-unhex-string (substring cur (match-end 0) nil) t))
8805 (setq cur (assoc key retval))
8806 (if cur
8807 (setcdr cur (cons val (cdr cur)))
8808 (setq retval (cons (list key val) retval)))))
8809 retval))
8810
8811;;;###autoload
8812(defun message-mailto ()
8813 "Command to parse command line mailto: links.
8814This is meant to be used for MIME handlers: Setting the handler
8815for \"x-scheme-handler/mailto;\" to \"emacs -f message-mailto %u\"
8816will then start up Emacs ready to compose mail."
8817 (interactive)
8818 ;; <a href="mailto:someone@example.com?subject=This%20is%20the%20subject&cc=someone_else@example.com&body=This%20is%20the%20body">Send email</a>
8819 (message-mail)
8820 (message-mailto-1 (pop command-line-args-left)))
8821
8822(defun message-mailto-1 (url)
8823 (let ((args (message-parse-mailto-url url)))
8824 (dolist (arg args)
8825 (unless (equal (car arg) "body")
8826 (message-position-on-field (capitalize (car arg)))
8827 (insert (replace-regexp-in-string
8828 "\r\n" "\n"
8829 (mapconcat #'identity (reverse (cdr arg)) ", ") nil t))))
8830 (when (assoc "body" args)
8831 (message-goto-body)
8832 (dolist (body (cdr (assoc "body" args)))
8833 (insert body "\n")))
8834 (if (assoc "subject" args)
8835 (message-goto-body)
8836 (message-goto-subject))))
8837
8655(provide 'message) 8838(provide 'message)
8656 8839
8657(run-hooks 'message-load-hook) 8840(run-hooks 'message-load-hook)
diff --git a/lisp/gnus/mm-decode.el b/lisp/gnus/mm-decode.el
index 587c4e01b92..7f8ab5f9ef5 100644
--- a/lisp/gnus/mm-decode.el
+++ b/lisp/gnus/mm-decode.el
@@ -1680,6 +1680,12 @@ If RECURSIVE, search recursively."
1680 (t (y-or-n-p 1680 (t (y-or-n-p
1681 (format "Decrypt (S/MIME) part? ")))) 1681 (format "Decrypt (S/MIME) part? "))))
1682 (mm-view-pkcs7 parts from)) 1682 (mm-view-pkcs7 parts from))
1683 (goto-char (point-min))
1684 ;; The encrypted document is a MIME part, and may use either
1685 ;; CRLF (Outlook and the like) or newlines for end-of-line
1686 ;; markers. Translate from CRLF.
1687 (while (search-forward "\r\n" nil t)
1688 (replace-match "\n"))
1683 ;; Normally there will be a Content-type header here, but 1689 ;; Normally there will be a Content-type header here, but
1684 ;; some mailers don't add that to the encrypted part, which 1690 ;; some mailers don't add that to the encrypted part, which
1685 ;; makes the subsequent re-dissection fail here. 1691 ;; makes the subsequent re-dissection fail here.
diff --git a/lisp/gnus/mm-view.el b/lisp/gnus/mm-view.el
index 828ac633dc5..bd5960c18b2 100644
--- a/lisp/gnus/mm-view.el
+++ b/lisp/gnus/mm-view.el
@@ -59,11 +59,16 @@
59 "The attributes of renderer types for text/html.") 59 "The attributes of renderer types for text/html.")
60 60
61(defcustom mm-fill-flowed t 61(defcustom mm-fill-flowed t
62 "If non-nil a format=flowed article will be displayed flowed." 62 "If non-nil, format=flowed articles will be displayed flowed."
63 :type 'boolean 63 :type 'boolean
64 :version "22.1" 64 :version "22.1"
65 :group 'mime-display) 65 :group 'mime-display)
66 66
67;; Not a defcustom, since it's usually overridden by the callers of
68;; the mm functions.
69(defvar mm-inline-font-lock t
70 "If non-nil, do font locking of inline media types that support it.")
71
67(defcustom mm-inline-large-images-proportion 0.9 72(defcustom mm-inline-large-images-proportion 0.9
68 "Maximum proportion large images can occupy in the buffer. 73 "Maximum proportion large images can occupy in the buffer.
69This is only used if `mm-inline-large-images' is set to 74This is only used if `mm-inline-large-images' is set to
@@ -502,7 +507,8 @@ If MODE is not set, try to find mode automatically."
502 (delay-mode-hooks (set-auto-mode)) 507 (delay-mode-hooks (set-auto-mode))
503 (setq mode major-mode))) 508 (setq mode major-mode)))
504 ;; Do not fontify if the guess mode is fundamental. 509 ;; Do not fontify if the guess mode is fundamental.
505 (unless (eq major-mode 'fundamental-mode) 510 (when (and (not (eq major-mode 'fundamental-mode))
511 mm-inline-font-lock)
506 (font-lock-ensure)))) 512 (font-lock-ensure))))
507 (setq text (buffer-string)) 513 (setq text (buffer-string))
508 (when (eq mode 'diff-mode) 514 (when (eq mode 'diff-mode)
diff --git a/lisp/gnus/mml-sec.el b/lisp/gnus/mml-sec.el
index 740e1d2b722..69852c381d6 100644
--- a/lisp/gnus/mml-sec.el
+++ b/lisp/gnus/mml-sec.el
@@ -665,8 +665,9 @@ The passphrase is read and cached."
665 (epg-user-id-string uid)))) 665 (epg-user-id-string uid))))
666 (equal (downcase (car (mail-header-parse-address 666 (equal (downcase (car (mail-header-parse-address
667 (epg-user-id-string uid)))) 667 (epg-user-id-string uid))))
668 (downcase (car (mail-header-parse-address 668 (downcase (or (car (mail-header-parse-address
669 recipient)))) 669 recipient))
670 recipient)))
670 (not (memq (epg-user-id-validity uid) 671 (not (memq (epg-user-id-validity uid)
671 '(revoked expired)))) 672 '(revoked expired))))
672 (throw 'break t)))))) 673 (throw 'break t))))))
@@ -937,6 +938,10 @@ If no one is selected, symmetric encryption will be performed. "
937 (signal (car error) (cdr error)))) 938 (signal (car error) (cdr error))))
938 cipher)) 939 cipher))
939 940
941;; Should probably be removed and the interface should be different.
942(defvar mml-secure-allow-signing-with-unknown-recipient nil
943 "Variable to bind to allow automatic recipient selection.")
944
940(defun mml-secure-epg-sign (protocol mode) 945(defun mml-secure-epg-sign (protocol mode)
941 ;; Based on code appearing inside mml2015-epg-sign. 946 ;; Based on code appearing inside mml2015-epg-sign.
942 (let* ((context (epg-make-context protocol)) 947 (let* ((context (epg-make-context protocol))
@@ -953,7 +958,8 @@ If no one is selected, symmetric encryption will be performed. "
953 ;; then there's no point advising the user to examine it. If 958 ;; then there's no point advising the user to examine it. If
954 ;; there are any other variables worth examining, please 959 ;; there are any other variables worth examining, please
955 ;; improve this error message by having it mention them. 960 ;; improve this error message by having it mention them.
956 (error "Couldn't find any signer names%s" maybe-msg))) 961 (unless mml-secure-allow-signing-with-unknown-recipient
962 (error "Couldn't find any signer names%s" maybe-msg))))
957 (when (eq 'OpenPGP protocol) 963 (when (eq 'OpenPGP protocol)
958 (setf (epg-context-armor context) t) 964 (setf (epg-context-armor context) t)
959 (setf (epg-context-textmode context) t) 965 (setf (epg-context-textmode context) t)
diff --git a/lisp/gnus/mml.el b/lisp/gnus/mml.el
index 21491499eb8..ef8aa6ac019 100644
--- a/lisp/gnus/mml.el
+++ b/lisp/gnus/mml.el
@@ -295,6 +295,17 @@ part. This is for the internal use, you should never modify the value.")
295 (t 295 (t
296 (mm-find-mime-charset-region point (point) 296 (mm-find-mime-charset-region point (point)
297 mm-hack-charsets)))) 297 mm-hack-charsets))))
298 ;; We have a part that already has a transfer encoding. Undo
299 ;; that so that we don't double-encode later.
300 (when (and raw
301 (cdr (assq 'data-encoding tag)))
302 (with-temp-buffer
303 (set-buffer-multibyte nil)
304 (insert contents)
305 (mm-decode-content-transfer-encoding
306 (intern (cdr (assq 'data-encoding tag)))
307 (cdr (assq 'type tag)))
308 (setq contents (buffer-string))))
298 (when (and (not raw) (memq nil charsets)) 309 (when (and (not raw) (memq nil charsets))
299 (if (or (memq 'unknown-encoding mml-confirmation-set) 310 (if (or (memq 'unknown-encoding mml-confirmation-set)
300 (message-options-get 'unknown-encoding) 311 (message-options-get 'unknown-encoding)
@@ -313,8 +324,8 @@ Message contains characters with unknown encoding. Really send? ")
313 (eq 'mml (car tag)) 324 (eq 'mml (car tag))
314 (< (length charsets) 2)) 325 (< (length charsets) 2))
315 (if (or (not no-markup-p) 326 (if (or (not no-markup-p)
327 ;; Don't create blank parts.
316 (string-match "[^ \t\r\n]" contents)) 328 (string-match "[^ \t\r\n]" contents))
317 ;; Don't create blank parts.
318 (push (nconc tag (list (cons 'contents contents))) 329 (push (nconc tag (list (cons 'contents contents)))
319 struct)) 330 struct))
320 (let ((nstruct (mml-parse-singlepart-with-multiple-charsets 331 (let ((nstruct (mml-parse-singlepart-with-multiple-charsets
diff --git a/lisp/gnus/smime.el b/lisp/gnus/smime.el
index fe6daf6b037..5500148e518 100644
--- a/lisp/gnus/smime.el
+++ b/lisp/gnus/smime.el
@@ -185,6 +185,9 @@ and the files themselves should be in PEM format."
185 :version "22.1" 185 :version "22.1"
186 :type '(choice (const :tag "Triple DES" "-des3") 186 :type '(choice (const :tag "Triple DES" "-des3")
187 (const :tag "DES" "-des") 187 (const :tag "DES" "-des")
188 (const :tag "AES 256 bits" "-aes256")
189 (const :tag "AES 192 bits" "-aes192")
190 (const :tag "AES 128 bits" "-aes128")
188 (const :tag "RC2 40 bits" "-rc2-40") 191 (const :tag "RC2 40 bits" "-rc2-40")
189 (const :tag "RC2 64 bits" "-rc2-64") 192 (const :tag "RC2 64 bits" "-rc2-64")
190 (const :tag "RC2 128 bits" "-rc2-128")) 193 (const :tag "RC2 128 bits" "-rc2-128"))
diff --git a/lisp/help-fns.el b/lisp/help-fns.el
index 082a44d9bf5..d40b9286f8e 100644
--- a/lisp/help-fns.el
+++ b/lisp/help-fns.el
@@ -1778,6 +1778,50 @@ documentation for the major and minor modes of that buffer."
1778 ;; For the sake of IELM and maybe others 1778 ;; For the sake of IELM and maybe others
1779 nil) 1779 nil)
1780 1780
1781;; Widgets.
1782
1783(defvar describe-widget-functions
1784 '(button-describe widget-describe)
1785 "A list of functions for `describe-widget' to call.
1786Each function should take one argument, a buffer position, and return
1787non-nil if it described a widget at that position.")
1788
1789;;;###autoload
1790(defun describe-widget (&optional pos)
1791 "Display a buffer with information about a widget.
1792You can use this command to describe buttons (e.g., the links in a *Help*
1793buffer), editable fields of the customization buffers, etc.
1794
1795Interactively, click on a widget to describe it, or hit RET to describe the
1796widget at point.
1797
1798When called from Lisp, POS may be a buffer position or a mouse position list.
1799
1800Calls each function of the list `describe-widget-functions' in turn, until
1801one of them returns non-nil."
1802 (interactive
1803 (list
1804 (let ((key
1805 (read-key
1806 "Click on a widget, or hit RET to describe the widget at point")))
1807 (cond ((eq key ?\C-m) (point))
1808 ((and (mouse-event-p key)
1809 (eq (event-basic-type key) 'mouse-1)
1810 (equal (event-modifiers key) '(click)))
1811 (event-end key))
1812 ((eq key ?\C-g) (signal 'quit nil))
1813 (t (user-error "You didn't specify a widget"))))))
1814 (let (buf)
1815 ;; Allow describing a widget in a different window.
1816 (when (posnp pos)
1817 (setq buf (window-buffer (posn-window pos))
1818 pos (posn-point pos)))
1819 (with-current-buffer (or buf (current-buffer))
1820 (unless (cl-some (lambda (fun) (when (fboundp fun) (funcall fun pos)))
1821 describe-widget-functions)
1822 (message "No widget found at that position")))))
1823
1824
1781;;; Replacements for old lib-src/ programs. Don't seem especially useful. 1825;;; Replacements for old lib-src/ programs. Don't seem especially useful.
1782 1826
1783;; Replaces lib-src/digest-doc.c. 1827;; Replaces lib-src/digest-doc.c.
diff --git a/lisp/hi-lock.el b/lisp/hi-lock.el
index a18310322ad..33ca40f8dec 100644
--- a/lisp/hi-lock.el
+++ b/lisp/hi-lock.el
@@ -812,7 +812,9 @@ SPACES-REGEXP is a regexp to substitute spaces in font-lock search."
812 (setq hi-lock-interactive-patterns 812 (setq hi-lock-interactive-patterns
813 (cdr hi-lock-interactive-patterns) 813 (cdr hi-lock-interactive-patterns)
814 hi-lock-interactive-lighters 814 hi-lock-interactive-lighters
815 (cdr hi-lock-interactive-lighters))))))))) 815 (cdr hi-lock-interactive-lighters))))
816 (when (or (> search-start (point-min)) (< search-end (point-max)))
817 (message "Hi-lock added only in range %d-%d" search-start search-end)))))))
816 818
817(defun hi-lock-set-file-patterns (patterns) 819(defun hi-lock-set-file-patterns (patterns)
818 "Replace file patterns list with PATTERNS and refontify." 820 "Replace file patterns list with PATTERNS and refontify."
diff --git a/lisp/ibuf-ext.el b/lisp/ibuf-ext.el
index bfb9787a96d..c9ca1f87424 100644
--- a/lisp/ibuf-ext.el
+++ b/lisp/ibuf-ext.el
@@ -504,7 +504,7 @@ format. See `ibuffer-update-saved-filters-format' and
504 (ibuffer-forward-line 0)) 504 (ibuffer-forward-line 0))
505 505
506(defun ibuffer--maybe-erase-shell-cmd-output () 506(defun ibuffer--maybe-erase-shell-cmd-output ()
507 (let ((buf (get-buffer "*Shell Command Output*"))) 507 (let ((buf (get-buffer shell-command-buffer-name)))
508 (when (and (buffer-live-p buf) 508 (when (and (buffer-live-p buf)
509 (not shell-command-dont-erase-buffer) 509 (not shell-command-dont-erase-buffer)
510 (not (zerop (buffer-size buf)))) 510 (not (zerop (buffer-size buf))))
@@ -517,7 +517,7 @@ format. See `ibuffer-update-saved-filters-format' and
517 :opstring "Shell command executed on" 517 :opstring "Shell command executed on"
518 :before (ibuffer--maybe-erase-shell-cmd-output) 518 :before (ibuffer--maybe-erase-shell-cmd-output)
519 :modifier-p nil) 519 :modifier-p nil)
520 (let ((out-buf (get-buffer-create "*Shell Command Output*"))) 520 (let ((out-buf (get-buffer-create shell-command-buffer-name)))
521 (with-current-buffer out-buf (goto-char (point-max))) 521 (with-current-buffer out-buf (goto-char (point-max)))
522 (call-shell-region (point-min) (point-max) 522 (call-shell-region (point-min) (point-max)
523 command nil out-buf))) 523 command nil out-buf)))
@@ -542,7 +542,7 @@ format. See `ibuffer-update-saved-filters-format' and
542 :modifier-p nil) 542 :modifier-p nil)
543 (let ((file (and (not (buffer-modified-p)) 543 (let ((file (and (not (buffer-modified-p))
544 buffer-file-name)) 544 buffer-file-name))
545 (out-buf (get-buffer-create "*Shell Command Output*"))) 545 (out-buf (get-buffer-create shell-command-buffer-name)))
546 (unless (and file (file-exists-p file)) 546 (unless (and file (file-exists-p file))
547 (setq file 547 (setq file
548 (make-temp-file 548 (make-temp-file
diff --git a/lisp/image-file.el b/lisp/image-file.el
index 89cd75d50dd..22366c89e6a 100644
--- a/lisp/image-file.el
+++ b/lisp/image-file.el
@@ -32,6 +32,7 @@
32;;; Code: 32;;; Code:
33 33
34(require 'image) 34(require 'image)
35(require 'image-converter)
35 36
36 37
37;;;###autoload 38;;;###autoload
@@ -80,10 +81,13 @@ the variable is set using \\[customize]."
80 (let ((exts-regexp 81 (let ((exts-regexp
81 (and image-file-name-extensions 82 (and image-file-name-extensions
82 (concat "\\." 83 (concat "\\."
83 (regexp-opt (nconc (mapcar #'upcase 84 (regexp-opt
84 image-file-name-extensions) 85 (append (mapcar #'upcase image-file-name-extensions)
85 image-file-name-extensions) 86 image-file-name-extensions
86 t) 87 (mapcar #'upcase
88 image-converter-file-name-extensions)
89 image-converter-file-name-extensions)
90 t)
87 "\\'")))) 91 "\\'"))))
88 (mapconcat 92 (mapconcat
89 'identity 93 'identity
diff --git a/lisp/image-mode.el b/lisp/image-mode.el
index 1bb213c2489..948e62e10d0 100644
--- a/lisp/image-mode.el
+++ b/lisp/image-mode.el
@@ -40,6 +40,7 @@
40 40
41(require 'image) 41(require 'image)
42(require 'exif) 42(require 'exif)
43(require 'dired)
43(eval-when-compile (require 'cl-lib)) 44(eval-when-compile (require 'cl-lib))
44 45
45;;; Image mode window-info management. 46;;; Image mode window-info management.
@@ -614,21 +615,23 @@ Key bindings:
614 (if (not (image-get-display-property)) 615 (if (not (image-get-display-property))
615 (progn 616 (progn
616 (when (condition-case err 617 (when (condition-case err
617 (progn 618 (progn
618 (image-toggle-display-image) 619 (image-toggle-display-image)
619 t) 620 t)
620 (unknown-image-type 621 (unknown-image-type
621 (image-mode-as-text) 622 (image-mode-as-text)
622 (funcall 623 (funcall
623 (if (called-interactively-p 'any) 'error 'message) 624 (if (called-interactively-p 'any) 'error 'message)
624 "Unknown image type; consider switching `image-use-external-converter' on") 625 (if image-use-external-converter
625 nil) 626 "Unknown image type"
626 (error 627 "Unknown image type; consider switching `image-use-external-converter' on"))
627 (image-mode-as-text) 628 nil)
628 (funcall 629 (error
629 (if (called-interactively-p 'any) 'error 'message) 630 (image-mode-as-text)
630 "Cannot display image: %s" (cdr err)) 631 (funcall
631 nil)) 632 (if (called-interactively-p 'any) 'error 'message)
633 "Cannot display image: %s" (cdr err))
634 nil))
632 ;; If attempt to display the image fails. 635 ;; If attempt to display the image fails.
633 (if (not (image-get-display-property)) 636 (if (not (image-get-display-property))
634 (error "Invalid image")) 637 (error "Invalid image"))
@@ -816,13 +819,21 @@ was inserted."
816 (- (nth 2 edges) (nth 0 edges)))) 819 (- (nth 2 edges) (nth 0 edges))))
817 (max-height (when edges 820 (max-height (when edges
818 (- (nth 3 edges) (nth 1 edges)))) 821 (- (nth 3 edges) (nth 1 edges))))
819 (type (if (image--imagemagick-wanted-p filename)
820 'imagemagick
821 (image-type file-or-data nil data-p)))
822 (inhibit-read-only t) 822 (inhibit-read-only t)
823 (buffer-undo-list t) 823 (buffer-undo-list t)
824 (modified (buffer-modified-p)) 824 (modified (buffer-modified-p))
825 props image) 825 props image type)
826
827 ;; If the data in the current buffer isn't from an existing file,
828 ;; but we have a file name (this happens when visiting images from
829 ;; a zip file, for instance), provide a type hint based on the
830 ;; suffix.
831 (when (and data-p filename)
832 (setq data-p (intern (format "image/%s"
833 (file-name-extension filename)))))
834 (setq type (if (image--imagemagick-wanted-p filename)
835 'imagemagick
836 (image-type file-or-data nil data-p)))
826 837
827 ;; Get the rotation data from the file, if any. 838 ;; Get the rotation data from the file, if any.
828 (when (zerop image-transform-rotation) ; don't reset modified value 839 (when (zerop image-transform-rotation) ; don't reset modified value
@@ -839,10 +850,13 @@ was inserted."
839 ;; :scale 1: If we do not set this, create-image will apply 850 ;; :scale 1: If we do not set this, create-image will apply
840 ;; default scaling based on font size. 851 ;; default scaling based on font size.
841 (setq image (if (not edges) 852 (setq image (if (not edges)
842 (create-image file-or-data type data-p :scale 1) 853 (create-image file-or-data type data-p :scale 1
854 :format (and filename data-p))
843 (create-image file-or-data type data-p :scale 1 855 (create-image file-or-data type data-p :scale 1
844 :max-width max-width 856 :max-width max-width
845 :max-height max-height))) 857 :max-height max-height
858 ;; Type hint.
859 :format (and filename data-p))))
846 860
847 ;; Discard any stale image data before looking it up again. 861 ;; Discard any stale image data before looking it up again.
848 (image-flush image) 862 (image-flush image)
@@ -1072,28 +1086,87 @@ replacing the current Image mode buffer."
1072 (error "The buffer is not in Image mode")) 1086 (error "The buffer is not in Image mode"))
1073 (unless buffer-file-name 1087 (unless buffer-file-name
1074 (error "The current image is not associated with a file")) 1088 (error "The current image is not associated with a file"))
1075 (let* ((file (file-name-nondirectory buffer-file-name)) 1089 (let ((next (image-mode--next-file buffer-file-name n)))
1076 (images (image-mode--images-in-directory file)) 1090 (unless next
1077 (idx 0)) 1091 (user-error "No %s file in this directory"
1078 (catch 'image-visit-next-file 1092 (if (> n 0)
1079 (dolist (f images) 1093 "next"
1080 (if (string= f file) 1094 "prev")))
1081 (throw 'image-visit-next-file (1+ idx))) 1095 (if (stringp next)
1082 (setq idx (1+ idx)))) 1096 (find-alternate-file next)
1083 (setq idx (mod (+ idx (or n 1)) (length images))) 1097 (funcall next))))
1084 (let ((image (nth idx images)) 1098
1085 (dir (file-name-directory buffer-file-name))) 1099(defun image-mode--directory-buffers (file)
1086 (find-alternate-file image) 1100 "Return a alist of type/buffer for all \"parent\" buffers to image FILE.
1087 ;; If we have dired buffer(s) open to where this image is, then 1101This is normally a list of dired buffers, but can also be archive and
1088 ;; place point on it. 1102tar mode buffers."
1103 (let ((buffers nil)
1104 (dir (file-name-directory file)))
1105 (cond
1106 ((and (boundp 'tar-superior-buffer)
1107 tar-superior-buffer)
1108 (when (buffer-live-p tar-superior-buffer)
1109 (push (cons 'tar tar-superior-buffer) buffers)))
1110 ((and (boundp 'archive-superior-buffer)
1111 archive-superior-buffer)
1112 (when (buffer-live-p archive-superior-buffer)
1113 (push (cons 'archive archive-superior-buffer) buffers)))
1114 (t
1115 ;; Find a dired buffer.
1089 (dolist (buffer (buffer-list)) 1116 (dolist (buffer (buffer-list))
1090 (with-current-buffer buffer 1117 (with-current-buffer buffer
1091 (when (and (derived-mode-p 'dired-mode) 1118 (when (and (derived-mode-p 'dired-mode)
1092 (equal (file-truename dir) 1119 (equal (file-truename dir)
1093 (file-truename default-directory))) 1120 (file-truename default-directory)))
1094 (save-window-excursion 1121 (push (cons 'dired (current-buffer)) buffers))))
1095 (switch-to-buffer (current-buffer) t t) 1122 ;; If we can't find any buffers to navigate in, we open a dired
1096 (dired-goto-file (expand-file-name image dir))))))))) 1123 ;; buffer.
1124 (unless buffers
1125 (push (cons 'dired (find-file-noselect dir)) buffers)
1126 (message "Opened a dired buffer on %s" dir))))
1127 buffers))
1128
1129(declare-function archive-next-file-displayer "arc-mode")
1130(declare-function tar-next-file-displayer "tar-mode")
1131
1132(defun image-mode--next-file (file n)
1133 "Go to the next image file in the parent buffer of FILE.
1134This is typically a dired buffer, but may also be a tar/archive buffer.
1135Return the next image file from that buffer.
1136If N is negative, go to the previous file."
1137 (let ((regexp (image-file-name-regexp))
1138 (buffers (image-mode--directory-buffers file))
1139 next)
1140 (dolist (buffer buffers)
1141 ;; We do this traversal for all the dired buffers open on this
1142 ;; directory. There probably is just one, but we want to move
1143 ;; point in all of them.
1144 (save-window-excursion
1145 (switch-to-buffer (cdr buffer) t t)
1146 (cl-case (car buffer)
1147 ('dired
1148 (dired-goto-file file)
1149 (let (found)
1150 (while (and (not found)
1151 ;; Stop if we reach the end/start of the buffer.
1152 (if (> n 0)
1153 (not (eobp))
1154 (not (bobp))))
1155 (dired-next-line n)
1156 (let ((candidate (dired-get-filename nil t)))
1157 (when (and candidate
1158 (string-match-p regexp candidate))
1159 (setq found candidate))))
1160 (if found
1161 (setq next found)
1162 ;; If we didn't find a next/prev file, then restore
1163 ;; point.
1164 (dired-goto-file file))))
1165 ('archive
1166 (setq next (archive-next-file-displayer file regexp n)))
1167 ('tar
1168 (setq next (tar-next-file-displayer file regexp n))))))
1169 next))
1097 1170
1098(defun image-previous-file (&optional n) 1171(defun image-previous-file (&optional n)
1099 "Visit the preceding image in the same directory as the current file. 1172 "Visit the preceding image in the same directory as the current file.
diff --git a/lisp/image/image-converter.el b/lisp/image/image-converter.el
index b694052f5b9..ee1dc845fb5 100644
--- a/lisp/image/image-converter.el
+++ b/lisp/image/image-converter.el
@@ -42,6 +42,9 @@ installed on the system."
42(defvar image-converter-regexp nil 42(defvar image-converter-regexp nil
43 "A regexp that matches the file name suffixes that can be converted.") 43 "A regexp that matches the file name suffixes that can be converted.")
44 44
45(defvar image-converter-file-name-extensions nil
46 "A list of file name suffixes that can be converted.")
47
45(defvar image-converter--converters 48(defvar image-converter--converters
46 '((graphicsmagick :command ("gm" "convert") :probe ("-list" "format")) 49 '((graphicsmagick :command ("gm" "convert") :probe ("-list" "format"))
47 (ffmpeg :command "ffmpeg" :probe "-decoders") 50 (ffmpeg :command "ffmpeg" :probe "-decoders")
@@ -58,9 +61,11 @@ is a string, it should be a MIME format string like
58 (unless image-converter 61 (unless image-converter
59 (image-converter--find-converter)) 62 (image-converter--find-converter))
60 ;; When image-converter was customized 63 ;; When image-converter was customized
61 (if (and image-converter (not image-converter-regexp)) 64 (when (and image-converter (not image-converter-regexp))
62 (when-let ((formats (image-converter--probe image-converter))) 65 (when-let ((formats (image-converter--probe image-converter)))
63 (setq image-converter-regexp (concat "\\." (regexp-opt formats) "\\'")))) 66 (setq image-converter-regexp
67 (concat "\\." (regexp-opt formats) "\\'"))
68 (setq image-converter-file-name-extensions formats)))
64 (and image-converter 69 (and image-converter
65 (or (and (not data-p) 70 (or (and (not data-p)
66 (string-match image-converter-regexp source)) 71 (string-match image-converter-regexp source))
@@ -183,7 +188,8 @@ data is returned as a string."
183 (dolist (elem image-converter--converters) 188 (dolist (elem image-converter--converters)
184 (when-let ((formats (image-converter--probe (car elem)))) 189 (when-let ((formats (image-converter--probe (car elem))))
185 (setq image-converter (car elem) 190 (setq image-converter (car elem)
186 image-converter-regexp (concat "\\." (regexp-opt formats) "\\'")) 191 image-converter-regexp (concat "\\." (regexp-opt formats) "\\'")
192 image-converter-file-name-extensions formats)
187 (throw 'done image-converter))))) 193 (throw 'done image-converter)))))
188 194
189(cl-defmethod image-converter--convert ((type (eql graphicsmagick)) source 195(cl-defmethod image-converter--convert ((type (eql graphicsmagick)) source
diff --git a/lisp/international/ja-dic-cnv.el b/lisp/international/ja-dic-cnv.el
index 45e13462656..f5e70ce7021 100644
--- a/lisp/international/ja-dic-cnv.el
+++ b/lisp/international/ja-dic-cnv.el
@@ -48,7 +48,7 @@
48(defvar ja-dic-filename "ja-dic.el") 48(defvar ja-dic-filename "ja-dic.el")
49 49
50(defun skkdic-convert-okuri-ari (skkbuf buf) 50(defun skkdic-convert-okuri-ari (skkbuf buf)
51 (byte-compile-info-message "Processing OKURI-ARI entries") 51 (byte-compile-info "Processing OKURI-ARI entries" t)
52 (goto-char (point-min)) 52 (goto-char (point-min))
53 (with-current-buffer buf 53 (with-current-buffer buf
54 (insert ";; Setting okuri-ari entries.\n" 54 (insert ";; Setting okuri-ari entries.\n"
@@ -97,7 +97,7 @@
97 ("ゆき" "行"))) 97 ("ゆき" "行")))
98 98
99(defun skkdic-convert-postfix (skkbuf buf) 99(defun skkdic-convert-postfix (skkbuf buf)
100 (byte-compile-info-message "Processing POSTFIX entries") 100 (byte-compile-info "Processing POSTFIX entries" t)
101 (goto-char (point-min)) 101 (goto-char (point-min))
102 (with-current-buffer buf 102 (with-current-buffer buf
103 (insert ";; Setting postfix entries.\n" 103 (insert ";; Setting postfix entries.\n"
@@ -151,7 +151,7 @@
151(defconst skkdic-prefix-list '(skkdic-prefix-list)) 151(defconst skkdic-prefix-list '(skkdic-prefix-list))
152 152
153(defun skkdic-convert-prefix (skkbuf buf) 153(defun skkdic-convert-prefix (skkbuf buf)
154 (byte-compile-info-message "Processing PREFIX entries") 154 (byte-compile-info "Processing PREFIX entries" t)
155 (goto-char (point-min)) 155 (goto-char (point-min))
156 (with-current-buffer buf 156 (with-current-buffer buf
157 (insert ";; Setting prefix entries.\n" 157 (insert ";; Setting prefix entries.\n"
@@ -273,7 +273,7 @@
273(defun skkdic-collect-okuri-nasi () 273(defun skkdic-collect-okuri-nasi ()
274 (save-excursion 274 (save-excursion
275 (let ((progress (make-progress-reporter 275 (let ((progress (make-progress-reporter
276 (byte-compile-info-message "Collecting OKURI-NASI entries") 276 (byte-compile-info "Collecting OKURI-NASI entries" t)
277 (point) (point-max) 277 (point) (point-max)
278 nil 10))) 278 nil 10)))
279 (while (re-search-forward "^\\(\\cH+\\) \\(/\\cj.*\\)/$" 279 (while (re-search-forward "^\\(\\cH+\\) \\(/\\cj.*\\)/$"
@@ -301,7 +301,7 @@
301 "(skkdic-set-okuri-nasi\n") 301 "(skkdic-set-okuri-nasi\n")
302 (let ((l (nreverse skkdic-okuri-nasi-entries)) 302 (let ((l (nreverse skkdic-okuri-nasi-entries))
303 (progress (make-progress-reporter 303 (progress (make-progress-reporter
304 (byte-compile-info-message "Processing OKURI-NASI entries") 304 (byte-compile-info "Processing OKURI-NASI entries" t)
305 0 skkdic-okuri-nasi-entries-count 305 0 skkdic-okuri-nasi-entries-count
306 nil 10)) 306 nil 10))
307 (count 0)) 307 (count 0))
@@ -531,8 +531,7 @@ To get complete usage, invoke:
531 ',(let ((l entries) 531 ',(let ((l entries)
532 (map '(skdic-okuri-nasi)) 532 (map '(skdic-okuri-nasi))
533 (progress (make-progress-reporter 533 (progress (make-progress-reporter
534 (byte-compile-info-message 534 (byte-compile-info "Extracting OKURI-NASI entries")
535 "Extracting OKURI-NASI entries")
536 0 (length entries))) 535 0 (length entries)))
537 (count 0) 536 (count 0)
538 entry) 537 entry)
diff --git a/lisp/mouse.el b/lisp/mouse.el
index 640f10af4e1..d369545f18e 100644
--- a/lisp/mouse.el
+++ b/lisp/mouse.el
@@ -49,7 +49,10 @@
49 "If non-nil, copy to kill-ring upon mouse adjustments of the region. 49 "If non-nil, copy to kill-ring upon mouse adjustments of the region.
50 50
51This affects `mouse-save-then-kill' (\\[mouse-save-then-kill]) in 51This affects `mouse-save-then-kill' (\\[mouse-save-then-kill]) in
52addition to mouse drags." 52addition to mouse drags.
53
54This variable applies only to mouse adjustments in Emacs, not
55selecting and adjusting regions in other windows."
53 :type 'boolean 56 :type 'boolean
54 :version "24.1") 57 :version "24.1")
55 58
diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el
index 8892e800cd6..2b8d4d0ce62 100644
--- a/lisp/net/browse-url.el
+++ b/lisp/net/browse-url.el
@@ -1,4 +1,4 @@
1;;; browse-url.el --- pass a URL to a WWW browser 1;;; browse-url.el --- pass a URL to a WWW browser -*- lexical-binding: t; -*-
2 2
3;; Copyright (C) 1995-2020 Free Software Foundation, Inc. 3;; Copyright (C) 1995-2020 Free Software Foundation, Inc.
4 4
@@ -425,25 +425,6 @@ Passing an interactive argument to \\[browse-url], or specific browser
425commands reverses the effect of this variable." 425commands reverses the effect of this variable."
426 :type 'boolean) 426 :type 'boolean)
427 427
428(defcustom browse-url-mosaic-program "xmosaic"
429 "The name by which to invoke Mosaic (or mMosaic)."
430 :type 'string
431 :version "20.3")
432
433(make-obsolete-variable 'browse-url-mosaic-program nil "25.1")
434
435(defcustom browse-url-mosaic-arguments nil
436 "A list of strings to pass to Mosaic as arguments."
437 :type '(repeat (string :tag "Argument")))
438
439(make-obsolete-variable 'browse-url-mosaic-arguments nil "25.1")
440
441(defcustom browse-url-mosaic-pidfile "~/.mosaicpid"
442 "The name of the pidfile created by Mosaic."
443 :type 'string)
444
445(make-obsolete-variable 'browse-url-mosaic-pidfile nil "25.1")
446
447(defcustom browse-url-conkeror-program "conkeror" 428(defcustom browse-url-conkeror-program "conkeror"
448 "The name by which to invoke Conkeror." 429 "The name by which to invoke Conkeror."
449 :type 'string 430 :type 'string
@@ -498,22 +479,6 @@ Used by the `browse-url-of-file' command."
498 "Hook run after `browse-url-of-file' has asked a browser to load a file." 479 "Hook run after `browse-url-of-file' has asked a browser to load a file."
499 :type 'hook) 480 :type 'hook)
500 481
501(defcustom browse-url-CCI-port 3003
502 "Port to access XMosaic via CCI.
503This can be any number between 1024 and 65535 but must correspond to
504the value set in the browser."
505 :type 'integer)
506
507(make-obsolete-variable 'browse-url-CCI-port nil "25.1")
508
509(defcustom browse-url-CCI-host "localhost"
510 "Host to access XMosaic via CCI.
511This should be the host name of the machine running XMosaic with CCI
512enabled. The port number should be set in `browse-url-CCI-port'."
513 :type 'string)
514
515(make-obsolete-variable 'browse-url-CCI-host nil "25.1")
516
517(defvar browse-url-temp-file-name nil) 482(defvar browse-url-temp-file-name nil)
518(make-variable-buffer-local 'browse-url-temp-file-name) 483(make-variable-buffer-local 'browse-url-temp-file-name)
519 484
@@ -622,7 +587,7 @@ process), or nil (we don't know)."
622 kind))) 587 kind)))
623 588
624(defun browse-url--mailto (url &rest args) 589(defun browse-url--mailto (url &rest args)
625 "Calls `browse-url-mailto-function' with URL and ARGS." 590 "Call `browse-url-mailto-function' with URL and ARGS."
626 (funcall browse-url-mailto-function url args)) 591 (funcall browse-url-mailto-function url args))
627 592
628(defun browse-url--browser-kind-mailto (url) 593(defun browse-url--browser-kind-mailto (url)
@@ -631,7 +596,7 @@ process), or nil (we don't know)."
631 #'browse-url--browser-kind-mailto) 596 #'browse-url--browser-kind-mailto)
632 597
633(defun browse-url--man (url &rest args) 598(defun browse-url--man (url &rest args)
634 "Calls `browse-url-man-function' with URL and ARGS." 599 "Call `browse-url-man-function' with URL and ARGS."
635 (funcall browse-url-man-function url args)) 600 (funcall browse-url-man-function url args))
636 601
637(defun browse-url--browser-kind-man (url) 602(defun browse-url--browser-kind-man (url)
@@ -640,7 +605,7 @@ process), or nil (we don't know)."
640 #'browse-url--browser-kind-man) 605 #'browse-url--browser-kind-man)
641 606
642(defun browse-url--browser (url &rest args) 607(defun browse-url--browser (url &rest args)
643 "Calls `browse-url-browser-function' with URL and ARGS." 608 "Call `browse-url-browser-function' with URL and ARGS."
644 (funcall browse-url-browser-function url args)) 609 (funcall browse-url-browser-function url args))
645 610
646(defun browse-url--browser-kind-browser (url) 611(defun browse-url--browser-kind-browser (url)
@@ -854,8 +819,8 @@ narrowed."
854 (browse-url-of-file file-name)))) 819 (browse-url-of-file file-name))))
855 820
856(defun browse-url-delete-temp-file (&optional temp-file-name) 821(defun browse-url-delete-temp-file (&optional temp-file-name)
857 ;; Delete browse-url-temp-file-name from the file system 822 "Delete `browse-url-temp-file-name' from the file system.
858 ;; If optional arg TEMP-FILE-NAME is non-nil, delete it instead 823If optional arg TEMP-FILE-NAME is non-nil, delete it instead."
859 (let ((file-name (or temp-file-name browse-url-temp-file-name))) 824 (let ((file-name (or temp-file-name browse-url-temp-file-name)))
860 (if (and file-name (file-exists-p file-name)) 825 (if (and file-name (file-exists-p file-name))
861 (delete-file file-name)))) 826 (delete-file file-name))))
@@ -1075,8 +1040,6 @@ instead of `browse-url-new-window-flag'."
1075;;; ((executable-find browse-url-galeon-program) 'browse-url-galeon) 1040;;; ((executable-find browse-url-galeon-program) 'browse-url-galeon)
1076 ((executable-find browse-url-kde-program) 'browse-url-kde) 1041 ((executable-find browse-url-kde-program) 'browse-url-kde)
1077;;; ((executable-find browse-url-netscape-program) 'browse-url-netscape) 1042;;; ((executable-find browse-url-netscape-program) 'browse-url-netscape)
1078;;; ((executable-find browse-url-mosaic-program) 'browse-url-mosaic)
1079;;; ((executable-find browse-url-conkeror-program) 'browse-url-conkeror)
1080 ((executable-find browse-url-chrome-program) 'browse-url-chrome) 1043 ((executable-find browse-url-chrome-program) 'browse-url-chrome)
1081 ((executable-find browse-url-xterm-program) 'browse-url-text-xterm) 1044 ((executable-find browse-url-xterm-program) 'browse-url-text-xterm)
1082 ((locate-library "w3") 'browse-url-w3) 1045 ((locate-library "w3") 'browse-url-w3)
@@ -1444,93 +1407,6 @@ used instead of `browse-url-new-window-flag'."
1444 1407
1445(function-put 'browse-url-gnome-moz 'browse-url-browser-kind 'external) 1408(function-put 'browse-url-gnome-moz 'browse-url-browser-kind 'external)
1446 1409
1447;; --- Mosaic ---
1448
1449;;;###autoload
1450(defun browse-url-mosaic (url &optional new-window)
1451 "Ask the XMosaic WWW browser to load URL.
1452
1453Default to the URL around or before point. The strings in variable
1454`browse-url-mosaic-arguments' are also passed to Mosaic and the
1455program is invoked according to the variable
1456`browse-url-mosaic-program'.
1457
1458When called interactively, if variable `browse-url-new-window-flag' is
1459non-nil, load the document in a new Mosaic window, otherwise use a
1460random existing one. A non-nil interactive prefix argument reverses
1461the effect of `browse-url-new-window-flag'.
1462
1463When called non-interactively, optional second argument NEW-WINDOW is
1464used instead of `browse-url-new-window-flag'."
1465 (declare (obsolete nil "25.1"))
1466 (interactive (browse-url-interactive-arg "Mosaic URL: "))
1467 (let ((pidfile (expand-file-name browse-url-mosaic-pidfile))
1468 pid)
1469 (if (file-readable-p pidfile)
1470 (with-temp-buffer
1471 (insert-file-contents pidfile)
1472 (setq pid (read (current-buffer)))))
1473 (if (and (integerp pid) (zerop (signal-process pid 0))) ; Mosaic running
1474 (progn
1475 (with-temp-buffer
1476 (insert (if (browse-url-maybe-new-window new-window)
1477 "newwin\n"
1478 "goto\n")
1479 url "\n")
1480 (with-file-modes ?\700
1481 (if (file-exists-p
1482 (setq pidfile (format "/tmp/Mosaic.%d" pid)))
1483 (delete-file pidfile))
1484 ;; https://debbugs.gnu.org/17428. Use O_EXCL.
1485 (write-region nil nil pidfile nil 'silent nil 'excl)))
1486 ;; Send signal SIGUSR to Mosaic
1487 (message "Signaling Mosaic...")
1488 (signal-process pid 'SIGUSR1)
1489 ;; Or you could try:
1490 ;; (call-process "kill" nil 0 nil "-USR1" (int-to-string pid))
1491 (message "Signaling Mosaic...done"))
1492 ;; Mosaic not running - start it
1493 (message "Starting %s..." browse-url-mosaic-program)
1494 (apply 'start-process "xmosaic" nil browse-url-mosaic-program
1495 (append browse-url-mosaic-arguments (list url)))
1496 (message "Starting %s...done" browse-url-mosaic-program))))
1497
1498(function-put 'browse-url-mosaic 'browse-url-browser-kind 'external)
1499
1500;; --- Mosaic using CCI ---
1501
1502;;;###autoload
1503(defun browse-url-cci (url &optional new-window)
1504 "Ask the XMosaic WWW browser to load URL.
1505Default to the URL around or before point.
1506
1507This function only works for XMosaic version 2.5 or later. You must
1508select `CCI' from XMosaic's File menu, set the CCI Port Address to the
1509value of variable `browse-url-CCI-port', and enable `Accept requests'.
1510
1511When called interactively, if variable `browse-url-new-window-flag' is
1512non-nil, load the document in a new browser window, otherwise use a
1513random existing one. A non-nil interactive prefix argument reverses
1514the effect of `browse-url-new-window-flag'.
1515
1516When called non-interactively, optional second argument NEW-WINDOW is
1517used instead of `browse-url-new-window-flag'."
1518 (declare (obsolete nil "25.1"))
1519 (interactive (browse-url-interactive-arg "Mosaic URL: "))
1520 (open-network-stream "browse-url" " *browse-url*"
1521 browse-url-CCI-host browse-url-CCI-port)
1522 ;; Todo: start browser if fails
1523 (process-send-string "browse-url"
1524 (concat "get url (" url ") output "
1525 (if (browse-url-maybe-new-window new-window)
1526 "new"
1527 "current")
1528 "\r\n"))
1529 (process-send-string "browse-url" "disconnect\r\n")
1530 (delete-process "browse-url"))
1531
1532(function-put 'browse-url-cci 'browse-url-browser-kind 'external)
1533
1534;; --- Conkeror --- 1410;; --- Conkeror ---
1535;;;###autoload 1411;;;###autoload
1536(defun browse-url-conkeror (url &optional new-window) 1412(defun browse-url-conkeror (url &optional new-window)
diff --git a/lisp/net/eww.el b/lisp/net/eww.el
index edb2f729c8b..e7170b3e6d1 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -277,6 +277,24 @@ This list can be customized via `eww-suggest-uris'."
277 (nreverse uris))) 277 (nreverse uris)))
278 278
279;;;###autoload 279;;;###autoload
280(defun eww-browse ()
281 "Function to be run to parse command line URLs.
282This is meant to be used for MIME handlers or command line use.
283
284Setting the handler for \"text/x-uri;\" to
285\"emacs -f eww-browse %u\" will then start up Emacs and call eww
286to browse the url.
287
288This can also be used on the command line directly:
289
290 emacs -f eww-browse https://gnu.org
291
292will start Emacs and browse the GNU web site."
293 (interactive)
294 (eww (pop command-line-args-left)))
295
296
297;;;###autoload
280(defun eww (url &optional arg buffer) 298(defun eww (url &optional arg buffer)
281 "Fetch URL and render the page. 299 "Fetch URL and render the page.
282If the input doesn't look like an URL or a domain name, the 300If the input doesn't look like an URL or a domain name, the
diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el
index 7e5af6910bb..88f5c2928e3 100644
--- a/lisp/net/tramp-adb.el
+++ b/lisp/net/tramp-adb.el
@@ -96,8 +96,10 @@ It is used for TCP/IP devices."
96(tramp--with-startup 96(tramp--with-startup
97 (add-to-list 'tramp-methods 97 (add-to-list 'tramp-methods
98 `(,tramp-adb-method 98 `(,tramp-adb-method
99 (tramp-tmpdir "/data/local/tmp") 99 (tramp-login-program ,tramp-adb-program)
100 (tramp-default-port 5555))) 100 (tramp-login-args (("shell")))
101 (tramp-tmpdir "/data/local/tmp")
102 (tramp-default-port 5555)))
101 103
102 (add-to-list 'tramp-default-host-alist `(,tramp-adb-method nil "")) 104 (add-to-list 'tramp-default-host-alist `(,tramp-adb-method nil ""))
103 105
@@ -885,158 +887,163 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored."
885;; The complete STDERR buffer is available only when the process has 887;; The complete STDERR buffer is available only when the process has
886;; terminated. 888;; terminated.
887(defun tramp-adb-handle-make-process (&rest args) 889(defun tramp-adb-handle-make-process (&rest args)
888 "Like `make-process' for Tramp files." 890 "Like `make-process' for Tramp files.
889 (when args 891If connection property \"direct-async-process\" is non-nil, an
890 (with-parsed-tramp-file-name (expand-file-name default-directory) nil 892alternative implementation will be used."
891 (let ((name (plist-get args :name)) 893 (if (tramp-get-connection-property
892 (buffer (plist-get args :buffer)) 894 (tramp-dissect-file-name default-directory) "direct-async-process" nil)
893 (command (plist-get args :command)) 895 (apply #'tramp-handle-make-process args)
894 (coding (plist-get args :coding)) 896 (when args
895 (noquery (plist-get args :noquery)) 897 (with-parsed-tramp-file-name (expand-file-name default-directory) nil
896 (connection-type (plist-get args :connection-type)) 898 (let ((name (plist-get args :name))
897 (filter (plist-get args :filter)) 899 (buffer (plist-get args :buffer))
898 (sentinel (plist-get args :sentinel)) 900 (command (plist-get args :command))
899 (stderr (plist-get args :stderr))) 901 (coding (plist-get args :coding))
900 (unless (stringp name) 902 (noquery (plist-get args :noquery))
901 (signal 'wrong-type-argument (list #'stringp name))) 903 (connection-type (plist-get args :connection-type))
902 (unless (or (null buffer) (bufferp buffer) (stringp buffer)) 904 (filter (plist-get args :filter))
903 (signal 'wrong-type-argument (list #'stringp buffer))) 905 (sentinel (plist-get args :sentinel))
904 (unless (consp command) 906 (stderr (plist-get args :stderr)))
905 (signal 'wrong-type-argument (list #'consp command))) 907 (unless (stringp name)
906 (unless (or (null coding) 908 (signal 'wrong-type-argument (list #'stringp name)))
907 (and (symbolp coding) (memq coding coding-system-list)) 909 (unless (or (null buffer) (bufferp buffer) (stringp buffer))
908 (and (consp coding) 910 (signal 'wrong-type-argument (list #'stringp buffer)))
909 (memq (car coding) coding-system-list) 911 (unless (consp command)
910 (memq (cdr coding) coding-system-list))) 912 (signal 'wrong-type-argument (list #'consp command)))
911 (signal 'wrong-type-argument (list #'symbolp coding))) 913 (unless (or (null coding)
912 (unless (or (null connection-type) (memq connection-type '(pipe pty))) 914 (and (symbolp coding) (memq coding coding-system-list))
913 (signal 'wrong-type-argument (list #'symbolp connection-type))) 915 (and (consp coding)
914 (unless (or (null filter) (functionp filter)) 916 (memq (car coding) coding-system-list)
915 (signal 'wrong-type-argument (list #'functionp filter))) 917 (memq (cdr coding) coding-system-list)))
916 (unless (or (null sentinel) (functionp sentinel)) 918 (signal 'wrong-type-argument (list #'symbolp coding)))
917 (signal 'wrong-type-argument (list #'functionp sentinel))) 919 (unless (or (null connection-type) (memq connection-type '(pipe pty)))
918 (unless (or (null stderr) (bufferp stderr) (stringp stderr)) 920 (signal 'wrong-type-argument (list #'symbolp connection-type)))
919 (signal 'wrong-type-argument (list #'stringp stderr))) 921 (unless (or (null filter) (functionp filter))
920 (when (and (stringp stderr) (tramp-tramp-file-p stderr) 922 (signal 'wrong-type-argument (list #'functionp filter)))
921 (not (tramp-equal-remote default-directory stderr))) 923 (unless (or (null sentinel) (functionp sentinel))
922 (signal 'file-error (list "Wrong stderr" stderr))) 924 (signal 'wrong-type-argument (list #'functionp sentinel)))
923 925 (unless (or (null stderr) (bufferp stderr) (stringp stderr))
924 (let* ((buffer 926 (signal 'wrong-type-argument (list #'stringp stderr)))
925 (if buffer 927 (when (and (stringp stderr) (tramp-tramp-file-p stderr)
926 (get-buffer-create buffer) 928 (not (tramp-equal-remote default-directory stderr)))
927 ;; BUFFER can be nil. We use a temporary buffer. 929 (signal 'file-error (list "Wrong stderr" stderr)))
928 (generate-new-buffer tramp-temp-buffer-name))) 930
929 ;; STDERR can also be a file name. 931 (let* ((buffer
930 (tmpstderr 932 (if buffer
931 (and stderr 933 (get-buffer-create buffer)
932 (if (and (stringp stderr) (tramp-tramp-file-p stderr)) 934 ;; BUFFER can be nil. We use a temporary buffer.
933 (tramp-unquote-file-local-name stderr) 935 (generate-new-buffer tramp-temp-buffer-name)))
934 (tramp-make-tramp-temp-file v)))) 936 ;; STDERR can also be a file name.
935 (remote-tmpstderr 937 (tmpstderr
936 (and tmpstderr (tramp-make-tramp-file-name v tmpstderr))) 938 (and stderr
937 (program (car command)) 939 (if (and (stringp stderr) (tramp-tramp-file-p stderr))
938 (args (cdr command)) 940 (tramp-unquote-file-local-name stderr)
939 (command 941 (tramp-make-tramp-temp-file v))))
940 (format "cd %s && exec %s %s" 942 (remote-tmpstderr
941 (tramp-shell-quote-argument localname) 943 (and tmpstderr (tramp-make-tramp-file-name v tmpstderr)))
942 (if tmpstderr (format "2>'%s'" tmpstderr) "") 944 (program (car command))
943 (mapconcat #'tramp-shell-quote-argument 945 (args (cdr command))
944 (cons program args) " "))) 946 (command
945 (tramp-process-connection-type 947 (format "cd %s && exec %s %s"
946 (or (null program) tramp-process-connection-type)) 948 (tramp-shell-quote-argument localname)
947 (bmp (and (buffer-live-p buffer) (buffer-modified-p buffer))) 949 (if tmpstderr (format "2>'%s'" tmpstderr) "")
948 (name1 name) 950 (mapconcat #'tramp-shell-quote-argument
949 (i 0)) 951 (cons program args) " ")))
950 952 (tramp-process-connection-type
951 (while (get-process name1) 953 (or (null program) tramp-process-connection-type))
952 ;; NAME must be unique as process name. 954 (bmp (and (buffer-live-p buffer) (buffer-modified-p buffer)))
953 (setq i (1+ i) 955 (name1 name)
954 name1 (format "%s<%d>" name i))) 956 (i 0))
955 (setq name name1) 957
956 ;; Set the new process properties. 958 (while (get-process name1)
957 (tramp-set-connection-property v "process-name" name) 959 ;; NAME must be unique as process name.
958 (tramp-set-connection-property v "process-buffer" buffer) 960 (setq i (1+ i)
959 961 name1 (format "%s<%d>" name i)))
960 (with-current-buffer (tramp-get-connection-buffer v) 962 (setq name name1)
961 (unwind-protect 963 ;; Set the new process properties.
962 ;; We catch this event. Otherwise, `make-process' 964 (tramp-set-connection-property v "process-name" name)
963 ;; could be called on the local host. 965 (tramp-set-connection-property v "process-buffer" buffer)
964 (save-excursion 966
965 (save-restriction 967 (with-current-buffer (tramp-get-connection-buffer v)
966 ;; Activate narrowing in order to save BUFFER 968 (unwind-protect
967 ;; contents. Clear also the modification time; 969 ;; We catch this event. Otherwise, `make-process'
968 ;; otherwise we might be interrupted by 970 ;; could be called on the local host.
969 ;; `verify-visited-file-modtime'. 971 (save-excursion
970 (let ((buffer-undo-list t) 972 (save-restriction
971 (inhibit-read-only t)) 973 ;; Activate narrowing in order to save BUFFER
972 (clear-visited-file-modtime) 974 ;; contents. Clear also the modification time;
973 (narrow-to-region (point-max) (point-max)) 975 ;; otherwise we might be interrupted by
974 ;; We call `tramp-adb-maybe-open-connection', in 976 ;; `verify-visited-file-modtime'.
975 ;; order to cleanup the prompt afterwards. 977 (let ((buffer-undo-list t)
976 (tramp-adb-maybe-open-connection v) 978 (inhibit-read-only t))
977 (delete-region (point-min) (point-max)) 979 (clear-visited-file-modtime)
978 ;; Send the command. 980 (narrow-to-region (point-max) (point-max))
979 (let* ((p (tramp-get-connection-process v))) 981 ;; We call `tramp-adb-maybe-open-connection',
980 (tramp-adb-send-command v command nil t) ; nooutput 982 ;; in order to cleanup the prompt afterwards.
981 ;; Set sentinel and filter. 983 (tramp-adb-maybe-open-connection v)
982 (when sentinel 984 (delete-region (point-min) (point-max))
983 (set-process-sentinel p sentinel)) 985 ;; Send the command.
984 (when filter 986 (let* ((p (tramp-get-connection-process v)))
985 (set-process-filter p filter)) 987 (tramp-adb-send-command v command nil t) ; nooutput
986 ;; Set query flag and process marker for this 988 ;; Set sentinel and filter.
987 ;; process. We ignore errors, because the 989 (when sentinel
988 ;; process could have finished already. 990 (set-process-sentinel p sentinel))
989 (ignore-errors 991 (when filter
990 (set-process-query-on-exit-flag p (null noquery)) 992 (set-process-filter p filter))
991 (set-marker (process-mark p) (point))) 993 ;; Set query flag and process marker for
992 ;; We must flush them here already; otherwise 994 ;; this process. We ignore errors, because
993 ;; `rename-file', `delete-file' or 995 ;; the process could have finished already.
994 ;; `insert-file-contents' will fail. 996 (ignore-errors
995 (tramp-flush-connection-property v "process-name") 997 (set-process-query-on-exit-flag p (null noquery))
996 (tramp-flush-connection-property v "process-buffer") 998 (set-marker (process-mark p) (point)))
997 ;; Copy tmpstderr file. 999 ;; We must flush them here already;
998 (when (and (stringp stderr) 1000 ;; otherwise `rename-file', `delete-file' or
999 (not (tramp-tramp-file-p stderr))) 1001 ;; `insert-file-contents' will fail.
1000 (add-function 1002 (tramp-flush-connection-property v "process-name")
1001 :after (process-sentinel p) 1003 (tramp-flush-connection-property v "process-buffer")
1002 (lambda (_proc _msg) 1004 ;; Copy tmpstderr file.
1003 (rename-file remote-tmpstderr stderr)))) 1005 (when (and (stringp stderr)
1004 ;; Read initial output. Remove the first line, 1006 (not (tramp-tramp-file-p stderr)))
1005 ;; which is the command echo. 1007 (add-function
1006 (while 1008 :after (process-sentinel p)
1007 (progn 1009 (lambda (_proc _msg)
1008 (goto-char (point-min)) 1010 (rename-file remote-tmpstderr stderr))))
1009 (not (re-search-forward "[\n]" nil t))) 1011 ;; Read initial output. Remove the first
1010 (tramp-accept-process-output p 0)) 1012 ;; line, which is the command echo.
1011 (delete-region (point-min) (point)) 1013 (while
1012 ;; Provide error buffer. This shows only 1014 (progn
1013 ;; initial error messages; messages arriving 1015 (goto-char (point-min))
1014 ;; later on will be inserted when the process 1016 (not (re-search-forward "[\n]" nil t)))
1015 ;; is deleted. The temporary file will exist 1017 (tramp-accept-process-output p 0))
1016 ;; until the process is deleted. 1018 (delete-region (point-min) (point))
1017 (when (bufferp stderr) 1019 ;; Provide error buffer. This shows only
1018 (with-current-buffer stderr 1020 ;; initial error messages; messages arriving
1019 (insert-file-contents-literally 1021 ;; later on will be inserted when the
1020 remote-tmpstderr 'visit)) 1022 ;; process is deleted. The temporary file
1021 ;; Delete tmpstderr file. 1023 ;; will exist until the process is deleted.
1022 (add-function 1024 (when (bufferp stderr)
1023 :after (process-sentinel p) 1025 (with-current-buffer stderr
1024 (lambda (_proc _msg) 1026 (insert-file-contents-literally
1025 (with-current-buffer stderr 1027 remote-tmpstderr 'visit))
1026 (insert-file-contents-literally 1028 ;; Delete tmpstderr file.
1027 remote-tmpstderr 'visit nil nil 'replace)) 1029 (add-function
1028 (delete-file remote-tmpstderr)))) 1030 :after (process-sentinel p)
1029 ;; Return process. 1031 (lambda (_proc _msg)
1030 p)))) 1032 (with-current-buffer stderr
1031 1033 (insert-file-contents-literally
1032 ;; Save exit. 1034 remote-tmpstderr 'visit nil nil 'replace))
1033 (if (string-match-p tramp-temp-buffer-name (buffer-name)) 1035 (delete-file remote-tmpstderr))))
1034 (ignore-errors 1036 ;; Return process.
1035 (set-process-buffer (tramp-get-connection-process v) nil) 1037 p))))
1036 (kill-buffer (current-buffer))) 1038
1037 (set-buffer-modified-p bmp)) 1039 ;; Save exit.
1038 (tramp-flush-connection-property v "process-name") 1040 (if (string-match-p tramp-temp-buffer-name (buffer-name))
1039 (tramp-flush-connection-property v "process-buffer")))))))) 1041 (ignore-errors
1042 (set-process-buffer (tramp-get-connection-process v) nil)
1043 (kill-buffer (current-buffer)))
1044 (set-buffer-modified-p bmp))
1045 (tramp-flush-connection-property v "process-name")
1046 (tramp-flush-connection-property v "process-buffer")))))))))
1040 1047
1041(defun tramp-adb-handle-exec-path () 1048(defun tramp-adb-handle-exec-path ()
1042 "Like `exec-path' for Tramp files." 1049 "Like `exec-path' for Tramp files."
@@ -1253,6 +1260,14 @@ connection if a previous connection has died for some reason."
1253 (tramp-adb-send-command 1260 (tramp-adb-send-command
1254 vec (format "PS1=\"///\"\"%s\"\"#$\"" prompt)) 1261 vec (format "PS1=\"///\"\"%s\"\"#$\"" prompt))
1255 1262
1263 ;; Disable line editing.
1264 (tramp-adb-send-command
1265 vec "set +o vi +o vi-esccomplete +o vi-tabcomplete +o emacs")
1266
1267 ;; Dump option settings in the traces.
1268 (when (>= tramp-verbose 9)
1269 (tramp-adb-send-command vec "set -o"))
1270
1256 ;; Check whether the properties have been changed. If 1271 ;; Check whether the properties have been changed. If
1257 ;; yes, this is a strong indication that we must expire all 1272 ;; yes, this is a strong indication that we must expire all
1258 ;; connection properties. We start again. 1273 ;; connection properties. We start again.
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el
index f9f0cbcc023..3e2eb023a33 100644
--- a/lisp/net/tramp-sh.el
+++ b/lisp/net/tramp-sh.el
@@ -2787,228 +2787,233 @@ the result will be a local, non-Tramp, file name."
2787;; terminated. 2787;; terminated.
2788(defun tramp-sh-handle-make-process (&rest args) 2788(defun tramp-sh-handle-make-process (&rest args)
2789 "Like `make-process' for Tramp files. 2789 "Like `make-process' for Tramp files.
2790STDERR can also be a file name." 2790STDERR can also be a file name. If connection property
2791 (when args 2791\"direct-async-process\" is non-nil, an alternative
2792 (with-parsed-tramp-file-name (expand-file-name default-directory) nil 2792implementation will be used."
2793 (let ((name (plist-get args :name)) 2793 (if (tramp-get-connection-property
2794 (buffer (plist-get args :buffer)) 2794 (tramp-dissect-file-name default-directory) "direct-async-process" nil)
2795 (command (plist-get args :command)) 2795 (apply #'tramp-handle-make-process args)
2796 (coding (plist-get args :coding)) 2796 (when args
2797 (noquery (plist-get args :noquery)) 2797 (with-parsed-tramp-file-name (expand-file-name default-directory) nil
2798 (connection-type (plist-get args :connection-type)) 2798 (let ((name (plist-get args :name))
2799 (filter (plist-get args :filter)) 2799 (buffer (plist-get args :buffer))
2800 (sentinel (plist-get args :sentinel)) 2800 (command (plist-get args :command))
2801 (stderr (plist-get args :stderr))) 2801 (coding (plist-get args :coding))
2802 (unless (stringp name) 2802 (noquery (plist-get args :noquery))
2803 (signal 'wrong-type-argument (list #'stringp name))) 2803 (connection-type (plist-get args :connection-type))
2804 (unless (or (null buffer) (bufferp buffer) (stringp buffer)) 2804 (filter (plist-get args :filter))
2805 (signal 'wrong-type-argument (list #'stringp buffer))) 2805 (sentinel (plist-get args :sentinel))
2806 (unless (consp command) 2806 (stderr (plist-get args :stderr)))
2807 (signal 'wrong-type-argument (list #'consp command))) 2807 (unless (stringp name)
2808 (unless (or (null coding) 2808 (signal 'wrong-type-argument (list #'stringp name)))
2809 (and (symbolp coding) (memq coding coding-system-list)) 2809 (unless (or (null buffer) (bufferp buffer) (stringp buffer))
2810 (and (consp coding) 2810 (signal 'wrong-type-argument (list #'stringp buffer)))
2811 (memq (car coding) coding-system-list) 2811 (unless (consp command)
2812 (memq (cdr coding) coding-system-list))) 2812 (signal 'wrong-type-argument (list #'consp command)))
2813 (signal 'wrong-type-argument (list #'symbolp coding))) 2813 (unless (or (null coding)
2814 (unless (or (null connection-type) (memq connection-type '(pipe pty))) 2814 (and (symbolp coding) (memq coding coding-system-list))
2815 (signal 'wrong-type-argument (list #'symbolp connection-type))) 2815 (and (consp coding)
2816 (unless (or (null filter) (functionp filter)) 2816 (memq (car coding) coding-system-list)
2817 (signal 'wrong-type-argument (list #'functionp filter))) 2817 (memq (cdr coding) coding-system-list)))
2818 (unless (or (null sentinel) (functionp sentinel)) 2818 (signal 'wrong-type-argument (list #'symbolp coding)))
2819 (signal 'wrong-type-argument (list #'functionp sentinel))) 2819 (unless (or (null connection-type) (memq connection-type '(pipe pty)))
2820 (unless (or (null stderr) (bufferp stderr) (stringp stderr)) 2820 (signal 'wrong-type-argument (list #'symbolp connection-type)))
2821 (signal 'wrong-type-argument (list #'stringp stderr))) 2821 (unless (or (null filter) (functionp filter))
2822 (when (and (stringp stderr) (tramp-tramp-file-p stderr) 2822 (signal 'wrong-type-argument (list #'functionp filter)))
2823 (not (tramp-equal-remote default-directory stderr))) 2823 (unless (or (null sentinel) (functionp sentinel))
2824 (signal 'file-error (list "Wrong stderr" stderr))) 2824 (signal 'wrong-type-argument (list #'functionp sentinel)))
2825 2825 (unless (or (null stderr) (bufferp stderr) (stringp stderr))
2826 (let* ((buffer 2826 (signal 'wrong-type-argument (list #'stringp stderr)))
2827 (if buffer 2827 (when (and (stringp stderr) (tramp-tramp-file-p stderr)
2828 (get-buffer-create buffer) 2828 (not (tramp-equal-remote default-directory stderr)))
2829 ;; BUFFER can be nil. We use a temporary buffer. 2829 (signal 'file-error (list "Wrong stderr" stderr)))
2830 (generate-new-buffer tramp-temp-buffer-name))) 2830
2831 ;; STDERR can also be a file name. 2831 (let* ((buffer
2832 (tmpstderr 2832 (if buffer
2833 (and stderr 2833 (get-buffer-create buffer)
2834 (if (and (stringp stderr) (tramp-tramp-file-p stderr)) 2834 ;; BUFFER can be nil. We use a temporary buffer.
2835 (tramp-unquote-file-local-name stderr) 2835 (generate-new-buffer tramp-temp-buffer-name)))
2836 (tramp-make-tramp-temp-file v)))) 2836 ;; STDERR can also be a file name.
2837 (remote-tmpstderr 2837 (tmpstderr
2838 (and tmpstderr (tramp-make-tramp-file-name v tmpstderr))) 2838 (and stderr
2839 (program (car command)) 2839 (if (and (stringp stderr) (tramp-tramp-file-p stderr))
2840 (args (cdr command)) 2840 (tramp-unquote-file-local-name stderr)
2841 ;; When PROGRAM matches "*sh", and the first arg is 2841 (tramp-make-tramp-temp-file v))))
2842 ;; "-c", it might be that the arguments exceed the 2842 (remote-tmpstderr
2843 ;; command line length. Therefore, we modify the 2843 (and tmpstderr (tramp-make-tramp-file-name v tmpstderr)))
2844 ;; command. 2844 (program (car command))
2845 (heredoc (and (stringp program) 2845 (args (cdr command))
2846 (string-match-p "sh$" program) 2846 ;; When PROGRAM matches "*sh", and the first arg is
2847 (string-equal "-c" (car args)) 2847 ;; "-c", it might be that the arguments exceed the
2848 (= (length args) 2))) 2848 ;; command line length. Therefore, we modify the
2849 ;; When PROGRAM is nil, we just provide a tty. 2849 ;; command.
2850 (args (if (not heredoc) args 2850 (heredoc (and (stringp program)
2851 (let ((i 250)) 2851 (string-match-p "sh$" program)
2852 (while (and (< i (length (cadr args))) 2852 (string-equal "-c" (car args))
2853 (string-match " " (cadr args) i)) 2853 (= (length args) 2)))
2854 (setcdr 2854 ;; When PROGRAM is nil, we just provide a tty.
2855 args 2855 (args (if (not heredoc) args
2856 (list 2856 (let ((i 250))
2857 (replace-match " \\\\\n" nil nil (cadr args)))) 2857 (while (and (< i (length (cadr args)))
2858 (setq i (+ i 250)))) 2858 (string-match " " (cadr args) i))
2859 (cdr args))) 2859 (setcdr
2860 ;; Use a human-friendly prompt, for example for 2860 args
2861 ;; `shell'. We discard hops, if existing, that's why 2861 (list
2862 ;; we cannot use `file-remote-p'. 2862 (replace-match " \\\\\n" nil nil (cadr args))))
2863 (prompt (format "PS1=%s %s" 2863 (setq i (+ i 250))))
2864 (tramp-make-tramp-file-name v nil 'nohop) 2864 (cdr args)))
2865 tramp-initial-end-of-output)) 2865 ;; Use a human-friendly prompt, for example for
2866 ;; We use as environment the difference to toplevel 2866 ;; `shell'. We discard hops, if existing, that's why
2867 ;; `process-environment'. 2867 ;; we cannot use `file-remote-p'.
2868 env uenv 2868 (prompt (format "PS1=%s %s"
2869 (env (dolist (elt (cons prompt process-environment) env) 2869 (tramp-make-tramp-file-name v nil 'nohop)
2870 (or (member 2870 tramp-initial-end-of-output))
2871 elt (default-toplevel-value 'process-environment)) 2871 ;; We use as environment the difference to toplevel
2872 (if (string-match-p "=" elt) 2872 ;; `process-environment'.
2873 (setq env (append env `(,elt))) 2873 env uenv
2874 (if (tramp-get-env-with-u-option v) 2874 (env (dolist (elt (cons prompt process-environment) env)
2875 (setq env (append `("-u" ,elt) env)) 2875 (or (member
2876 (setq uenv (cons elt uenv))))))) 2876 elt (default-toplevel-value 'process-environment))
2877 (command 2877 (if (string-match-p "=" elt)
2878 (when (stringp program) 2878 (setq env (append env `(,elt)))
2879 (setenv-internal 2879 (if (tramp-get-env-with-u-option v)
2880 env "INSIDE_EMACS" 2880 (setq env (append `("-u" ,elt) env))
2881 (concat (or (getenv "INSIDE_EMACS") emacs-version) 2881 (setq uenv (cons elt uenv)))))))
2882 ",tramp:" tramp-version) 2882 (command
2883 'keep) 2883 (when (stringp program)
2884 (format "cd %s && %s exec %s %s env %s %s" 2884 (setenv-internal
2885 (tramp-shell-quote-argument localname) 2885 env "INSIDE_EMACS"
2886 (if uenv 2886 (concat (or (getenv "INSIDE_EMACS") emacs-version)
2887 (format 2887 ",tramp:" tramp-version)
2888 "unset %s &&" 2888 'keep)
2889 (mapconcat 2889 (format "cd %s && %s exec %s %s env %s %s"
2890 #'tramp-shell-quote-argument uenv " ")) 2890 (tramp-shell-quote-argument localname)
2891 "") 2891 (if uenv
2892 (if heredoc (format "<<'%s'" tramp-end-of-heredoc) "") 2892 (format
2893 (if tmpstderr (format "2>'%s'" tmpstderr) "") 2893 "unset %s &&"
2894 (mapconcat #'tramp-shell-quote-argument env " ") 2894 (mapconcat
2895 (if heredoc 2895 #'tramp-shell-quote-argument uenv " "))
2896 (format "%s\n(\n%s\n) </dev/tty\n%s" 2896 "")
2897 program (car args) tramp-end-of-heredoc) 2897 (if heredoc (format "<<'%s'" tramp-end-of-heredoc) "")
2898 (mapconcat #'tramp-shell-quote-argument 2898 (if tmpstderr (format "2>'%s'" tmpstderr) "")
2899 (cons program args) " "))))) 2899 (mapconcat #'tramp-shell-quote-argument env " ")
2900 (tramp-process-connection-type 2900 (if heredoc
2901 (or (null program) tramp-process-connection-type)) 2901 (format "%s\n(\n%s\n) </dev/tty\n%s"
2902 (bmp (and (buffer-live-p buffer) (buffer-modified-p buffer))) 2902 program (car args) tramp-end-of-heredoc)
2903 (name1 name) 2903 (mapconcat #'tramp-shell-quote-argument
2904 (i 0) 2904 (cons program args) " ")))))
2905 ;; We do not want to raise an error when `make-process' 2905 (tramp-process-connection-type
2906 ;; has been started several times in `eshell' and 2906 (or (null program) tramp-process-connection-type))
2907 ;; friends. 2907 (bmp (and (buffer-live-p buffer) (buffer-modified-p buffer)))
2908 tramp-current-connection 2908 (name1 name)
2909 p) 2909 (i 0)
2910 2910 ;; We do not want to raise an error when
2911 (while (get-process name1) 2911 ;; `make-process' has been started several times in
2912 ;; NAME must be unique as process name. 2912 ;; `eshell' and friends.
2913 (setq i (1+ i) 2913 tramp-current-connection
2914 name1 (format "%s<%d>" name i))) 2914 p)
2915 (setq name name1) 2915
2916 ;; Set the new process properties. 2916 (while (get-process name1)
2917 (tramp-set-connection-property v "process-name" name) 2917 ;; NAME must be unique as process name.
2918 (tramp-set-connection-property v "process-buffer" buffer) 2918 (setq i (1+ i)
2919 2919 name1 (format "%s<%d>" name i)))
2920 (with-current-buffer (tramp-get-connection-buffer v) 2920 (setq name name1)
2921 (unwind-protect 2921 ;; Set the new process properties.
2922 ;; We catch this event. Otherwise, `make-process' could 2922 (tramp-set-connection-property v "process-name" name)
2923 ;; be called on the local host. 2923 (tramp-set-connection-property v "process-buffer" buffer)
2924 (save-excursion 2924
2925 (save-restriction 2925 (with-current-buffer (tramp-get-connection-buffer v)
2926 ;; Activate narrowing in order to save BUFFER 2926 (unwind-protect
2927 ;; contents. Clear also the modification time; 2927 ;; We catch this event. Otherwise, `make-process'
2928 ;; otherwise we might be interrupted by 2928 ;; could be called on the local host.
2929 ;; `verify-visited-file-modtime'. 2929 (save-excursion
2930 (let ((buffer-undo-list t) 2930 (save-restriction
2931 (inhibit-read-only t) 2931 ;; Activate narrowing in order to save BUFFER
2932 (mark (point-max))) 2932 ;; contents. Clear also the modification time;
2933 (clear-visited-file-modtime) 2933 ;; otherwise we might be interrupted by
2934 (narrow-to-region (point-max) (point-max)) 2934 ;; `verify-visited-file-modtime'.
2935 ;; We call `tramp-maybe-open-connection', in 2935 (let ((buffer-undo-list t)
2936 ;; order to cleanup the prompt afterwards. 2936 (inhibit-read-only t)
2937 (catch 'suppress 2937 (mark (point-max)))
2938 (tramp-maybe-open-connection v) 2938 (clear-visited-file-modtime)
2939 (setq p (tramp-get-connection-process v))
2940 ;; Set the pid of the remote shell. This is
2941 ;; needed when sending signals remotely.
2942 (let ((pid (tramp-send-command-and-read v "echo $$")))
2943 (process-put p 'remote-pid pid)
2944 (tramp-set-connection-property p "remote-pid" pid))
2945 ;; `tramp-maybe-open-connection' and
2946 ;; `tramp-send-command-and-read' could have
2947 ;; trashed the connection buffer. Remove this.
2948 (widen)
2949 (delete-region mark (point-max))
2950 (narrow-to-region (point-max) (point-max)) 2939 (narrow-to-region (point-max) (point-max))
2951 ;; Now do it. 2940 ;; We call `tramp-maybe-open-connection', in
2952 (if command 2941 ;; order to cleanup the prompt afterwards.
2953 ;; Send the command. 2942 (catch 'suppress
2954 (tramp-send-command v command nil t) ; nooutput 2943 (tramp-maybe-open-connection v)
2955 ;; Check, whether a pty is associated. 2944 (setq p (tramp-get-connection-process v))
2956 (unless (process-get p 'remote-tty) 2945 ;; Set the pid of the remote shell. This is
2957 (tramp-error 2946 ;; needed when sending signals remotely.
2958 v 'file-error 2947 (let ((pid (tramp-send-command-and-read v "echo $$")))
2959 "pty association is not supported for `%s'" 2948 (process-put p 'remote-pid pid)
2960 name)))) 2949 (tramp-set-connection-property p "remote-pid" pid))
2961 ;; Set sentinel and filter. 2950 ;; `tramp-maybe-open-connection' and
2962 (when sentinel 2951 ;; `tramp-send-command-and-read' could have
2963 (set-process-sentinel p sentinel)) 2952 ;; trashed the connection buffer. Remove this.
2964 (when filter 2953 (widen)
2965 (set-process-filter p filter)) 2954 (delete-region mark (point-max))
2966 ;; Set query flag and process marker for this 2955 (narrow-to-region (point-max) (point-max))
2967 ;; process. We ignore errors, because the 2956 ;; Now do it.
2968 ;; process could have finished already. 2957 (if command
2969 (ignore-errors 2958 ;; Send the command.
2970 (set-process-query-on-exit-flag p (null noquery)) 2959 (tramp-send-command v command nil t) ; nooutput
2971 (set-marker (process-mark p) (point))) 2960 ;; Check, whether a pty is associated.
2972 ;; We must flush them here already; otherwise 2961 (unless (process-get p 'remote-tty)
2973 ;; `rename-file', `delete-file' or 2962 (tramp-error
2974 ;; `insert-file-contents' will fail. 2963 v 'file-error
2975 (tramp-flush-connection-property v "process-name") 2964 "pty association is not supported for `%s'"
2976 (tramp-flush-connection-property v "process-buffer") 2965 name))))
2977 ;; Copy tmpstderr file. 2966 ;; Set sentinel and filter.
2978 (when (and (stringp stderr) 2967 (when sentinel
2979 (not (tramp-tramp-file-p stderr))) 2968 (set-process-sentinel p sentinel))
2980 (add-function 2969 (when filter
2981 :after (process-sentinel p) 2970 (set-process-filter p filter))
2982 (lambda (_proc _msg) 2971 ;; Set query flag and process marker for this
2983 (rename-file remote-tmpstderr stderr)))) 2972 ;; process. We ignore errors, because the
2984 ;; Provide error buffer. This shows only 2973 ;; process could have finished already.
2985 ;; initial error messages; messages arriving 2974 (ignore-errors
2986 ;; later on will be inserted when the process is 2975 (set-process-query-on-exit-flag p (null noquery))
2987 ;; deleted. The temporary file will exist until 2976 (set-marker (process-mark p) (point)))
2988 ;; the process is deleted. 2977 ;; We must flush them here already; otherwise
2989 (when (bufferp stderr) 2978 ;; `rename-file', `delete-file' or
2990 (with-current-buffer stderr 2979 ;; `insert-file-contents' will fail.
2991 (insert-file-contents-literally remote-tmpstderr)) 2980 (tramp-flush-connection-property v "process-name")
2992 ;; Delete tmpstderr file. 2981 (tramp-flush-connection-property v "process-buffer")
2993 (add-function 2982 ;; Copy tmpstderr file.
2994 :after (process-sentinel p) 2983 (when (and (stringp stderr)
2995 (lambda (_proc _msg) 2984 (not (tramp-tramp-file-p stderr)))
2996 (when (file-exists-p remote-tmpstderr) 2985 (add-function
2997 (with-current-buffer stderr 2986 :after (process-sentinel p)
2998 (insert-file-contents-literally 2987 (lambda (_proc _msg)
2999 remote-tmpstderr nil nil nil 'replace)) 2988 (rename-file remote-tmpstderr stderr))))
3000 (delete-file remote-tmpstderr))))) 2989 ;; Provide error buffer. This shows only
3001 ;; Return process. 2990 ;; initial error messages; messages arriving
3002 p))) 2991 ;; later on will be inserted when the process
2992 ;; is deleted. The temporary file will exist
2993 ;; until the process is deleted.
2994 (when (bufferp stderr)
2995 (with-current-buffer stderr
2996 (insert-file-contents-literally remote-tmpstderr))
2997 ;; Delete tmpstderr file.
2998 (add-function
2999 :after (process-sentinel p)
3000 (lambda (_proc _msg)
3001 (when (file-exists-p remote-tmpstderr)
3002 (with-current-buffer stderr
3003 (insert-file-contents-literally
3004 remote-tmpstderr nil nil nil 'replace))
3005 (delete-file remote-tmpstderr)))))
3006 ;; Return process.
3007 p)))
3003 3008
3004 ;; Save exit. 3009 ;; Save exit.
3005 (if (string-match-p tramp-temp-buffer-name (buffer-name)) 3010 (if (string-match-p tramp-temp-buffer-name (buffer-name))
3006 (ignore-errors 3011 (ignore-errors
3007 (set-process-buffer p nil) 3012 (set-process-buffer p nil)
3008 (kill-buffer (current-buffer))) 3013 (kill-buffer (current-buffer)))
3009 (set-buffer-modified-p bmp)) 3014 (set-buffer-modified-p bmp))
3010 (tramp-flush-connection-property v "process-name") 3015 (tramp-flush-connection-property v "process-name")
3011 (tramp-flush-connection-property v "process-buffer")))))))) 3016 (tramp-flush-connection-property v "process-buffer")))))))))
3012 3017
3013(defun tramp-sh-get-signal-strings (vec) 3018(defun tramp-sh-get-signal-strings (vec)
3014 "Strings to return by `process-file' in case of signals." 3019 "Strings to return by `process-file' in case of signals."
@@ -3646,6 +3651,14 @@ Fall back to normal file name handler if no Tramp handler exists."
3646 (save-match-data (apply (cdr fn) args)) 3651 (save-match-data (apply (cdr fn) args))
3647 (tramp-run-real-handler operation args))) 3652 (tramp-run-real-handler operation args)))
3648 3653
3654;;;###tramp-autoload
3655(defun tramp-sh-file-name-handler-p (vec)
3656 "Whether VEC uses a method from `tramp-sh-file-name-handler'."
3657 (and (assoc (tramp-file-name-method vec) tramp-methods)
3658 (eq (tramp-find-foreign-file-name-handler
3659 (tramp-make-tramp-file-name vec nil 'nohop))
3660 'tramp-sh-file-name-handler)))
3661
3649;; This must be the last entry, because `identity' always matches. 3662;; This must be the last entry, because `identity' always matches.
3650;;;###tramp-autoload 3663;;;###tramp-autoload
3651(tramp--with-startup 3664(tramp--with-startup
@@ -4769,6 +4782,12 @@ Goes through the list `tramp-inline-compress-commands'."
4769 (tramp-message 4782 (tramp-message
4770 vec 2 "Couldn't find an inline transfer compress command"))))) 4783 vec 2 "Couldn't find an inline transfer compress command")))))
4771 4784
4785;;;###tramp-autoload
4786(defun tramp-multi-hop-p (vec)
4787 "Whether the method of VEC is capable of multi-hops."
4788 (and (tramp-sh-file-name-handler-p vec)
4789 (not (tramp-get-method-parameter vec 'tramp-copy-program))))
4790
4772(defun tramp-compute-multi-hops (vec) 4791(defun tramp-compute-multi-hops (vec)
4773 "Expands VEC according to `tramp-default-proxies-alist'." 4792 "Expands VEC according to `tramp-default-proxies-alist'."
4774 (let ((saved-tdpa tramp-default-proxies-alist) 4793 (let ((saved-tdpa tramp-default-proxies-alist)
@@ -4832,8 +4851,7 @@ Goes through the list `tramp-inline-compress-commands'."
4832 (when (cdr target-alist) 4851 (when (cdr target-alist)
4833 (setq choices target-alist) 4852 (setq choices target-alist)
4834 (while (setq item (pop choices)) 4853 (while (setq item (pop choices))
4835 (when (or (not (tramp-get-method-parameter item 'tramp-login-program)) 4854 (unless (tramp-multi-hop-p item)
4836 (tramp-get-method-parameter item 'tramp-copy-program))
4837 (setq tramp-default-proxies-alist saved-tdpa) 4855 (setq tramp-default-proxies-alist saved-tdpa)
4838 (tramp-user-error 4856 (tramp-user-error
4839 vec "Method `%s' is not supported for multi-hops." 4857 vec "Method `%s' is not supported for multi-hops."
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index c169a86f915..fdf26f6b782 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -1482,10 +1482,7 @@ default values are used."
1482 (tramp-user-error 1482 (tramp-user-error
1483 v "Method `%s' is not known." method)) 1483 v "Method `%s' is not known." method))
1484 ;; Only some methods from tramp-sh.el do support multi-hops. 1484 ;; Only some methods from tramp-sh.el do support multi-hops.
1485 (when (and 1485 (unless (or (null hop) nodefault non-essential (tramp-multi-hop-p v))
1486 hop
1487 (or (not (tramp-get-method-parameter v 'tramp-login-program))
1488 (tramp-get-method-parameter v 'tramp-copy-program)))
1489 (tramp-user-error 1486 (tramp-user-error
1490 v "Method `%s' is not supported for multi-hops." method))))))) 1487 v "Method `%s' is not supported for multi-hops." method)))))))
1491 1488
@@ -1499,8 +1496,7 @@ See `tramp-dissect-file-name' for details."
1499 tramp-postfix-host-format name)) 1496 tramp-postfix-host-format name))
1500 nodefault))) 1497 nodefault)))
1501 ;; Only some methods from tramp-sh.el do support multi-hops. 1498 ;; Only some methods from tramp-sh.el do support multi-hops.
1502 (when (or (not (tramp-get-method-parameter v 'tramp-login-program)) 1499 (unless (or nodefault non-essential (tramp-multi-hop-p v))
1503 (tramp-get-method-parameter v 'tramp-copy-program))
1504 (tramp-user-error 1500 (tramp-user-error
1505 v "Method `%s' is not supported for multi-hops." 1501 v "Method `%s' is not supported for multi-hops."
1506 (tramp-file-name-method v))) 1502 (tramp-file-name-method v)))
@@ -3519,13 +3515,10 @@ User is always nil."
3519 3515
3520 ;; When we shall insert only a part of the file, we 3516 ;; When we shall insert only a part of the file, we
3521 ;; copy this part. This works only for the shell file 3517 ;; copy this part. This works only for the shell file
3522 ;; name handlers. 3518 ;; name handlers. It doesn't work for crypted files.
3523 (when (and (or beg end) 3519 (when (and (or beg end)
3524 ;; Direct actions aren't possible for 3520 (tramp-sh-file-name-handler-p v)
3525 ;; crypted directories. 3521 (null tramp-crypt-enabled))
3526 (null tramp-crypt-enabled)
3527 (tramp-get-method-parameter
3528 v 'tramp-login-program))
3529 (setq remote-copy (tramp-make-tramp-temp-file v)) 3522 (setq remote-copy (tramp-make-tramp-temp-file v))
3530 ;; This is defined in tramp-sh.el. Let's assume 3523 ;; This is defined in tramp-sh.el. Let's assume
3531 ;; this is loaded already. 3524 ;; this is loaded already.
@@ -3640,6 +3633,152 @@ User is always nil."
3640 (load local-copy noerror t nosuffix must-suffix) 3633 (load local-copy noerror t nosuffix must-suffix)
3641 (delete-file local-copy))))) 3634 (delete-file local-copy)))))
3642 t))) 3635 t)))
3636;; We use BUFFER also as connection buffer during setup. Because of
3637;; this, its original contents must be saved, and restored once
3638;; connection has been setup.
3639(defun tramp-handle-make-process (&rest args)
3640 "An alternative `make-process' implementation for Tramp files."
3641 (when args
3642 (with-parsed-tramp-file-name (expand-file-name default-directory) nil
3643 (let ((name (plist-get args :name))
3644 (buffer (plist-get args :buffer))
3645 (command (plist-get args :command))
3646 (coding (plist-get args :coding))
3647 (noquery (plist-get args :noquery))
3648 (connection-type (plist-get args :connection-type))
3649 (filter (plist-get args :filter))
3650 (sentinel (plist-get args :sentinel))
3651 (stderr (plist-get args :stderr)))
3652 (unless (stringp name)
3653 (signal 'wrong-type-argument (list #'stringp name)))
3654 (unless (or (null buffer) (bufferp buffer) (stringp buffer))
3655 (signal 'wrong-type-argument (list #'stringp buffer)))
3656 (unless (consp command)
3657 (signal 'wrong-type-argument (list #'consp command)))
3658 (unless (or (null coding)
3659 (and (symbolp coding) (memq coding coding-system-list))
3660 (and (consp coding)
3661 (memq (car coding) coding-system-list)
3662 (memq (cdr coding) coding-system-list)))
3663 (signal 'wrong-type-argument (list #'symbolp coding)))
3664 (unless (or (null connection-type) (memq connection-type '(pipe pty)))
3665 (signal 'wrong-type-argument (list #'symbolp connection-type)))
3666 (unless (or (null filter) (functionp filter))
3667 (signal 'wrong-type-argument (list #'functionp filter)))
3668 (unless (or (null sentinel) (functionp sentinel))
3669 (signal 'wrong-type-argument (list #'functionp sentinel)))
3670 (unless (or (null stderr) (bufferp stderr) (stringp stderr))
3671 (signal 'wrong-type-argument (list #'stringp stderr)))
3672 (when (and (stringp stderr) (tramp-tramp-file-p stderr)
3673 (not (tramp-equal-remote default-directory stderr)))
3674 (signal 'file-error (list "Wrong stderr" stderr)))
3675
3676 (let* ((buffer
3677 (if buffer
3678 (get-buffer-create buffer)
3679 ;; BUFFER can be nil. We use a temporary buffer.
3680 (generate-new-buffer tramp-temp-buffer-name)))
3681 (command (append `("cd" ,localname "&&")
3682 (mapcar #'tramp-shell-quote-argument command)))
3683 (bmp (and (buffer-live-p buffer) (buffer-modified-p buffer)))
3684 (name1 name)
3685 (i 0)
3686 ;; We do not want to raise an error when `make-process'
3687 ;; has been started several times in `eshell' and
3688 ;; friends.
3689 tramp-current-connection
3690 p)
3691
3692 (while (get-process name1)
3693 ;; NAME must be unique as process name.
3694 (setq i (1+ i)
3695 name1 (format "%s<%d>" name i)))
3696 (setq name name1)
3697 ;; Set the new process properties.
3698 (tramp-set-connection-property v "process-name" name)
3699 (tramp-set-connection-property v "process-buffer" buffer)
3700
3701 (with-current-buffer (tramp-get-connection-buffer v)
3702 (unwind-protect
3703 (let* ((login-program
3704 (tramp-get-method-parameter v 'tramp-login-program))
3705 (login-args
3706 (tramp-get-method-parameter v 'tramp-login-args))
3707 (async-args
3708 (tramp-get-method-parameter v 'tramp-async-args))
3709 ;; We don't create the temporary file. In
3710 ;; fact, it is just a prefix for the
3711 ;; ControlPath option of ssh; the real
3712 ;; temporary file has another name, and it is
3713 ;; created and protected by ssh. It is also
3714 ;; removed by ssh when the connection is
3715 ;; closed. The temporary file name is cached
3716 ;; in the main connection process, therefore
3717 ;; we cannot use `tramp-get-connection-process'.
3718 (tmpfile
3719 (when (tramp-sh-file-name-handler-p v)
3720 (with-tramp-connection-property
3721 (tramp-get-process v) "temp-file"
3722 (tramp-compat-make-temp-name))))
3723 (options
3724 (when (tramp-sh-file-name-handler-p v)
3725 (tramp-compat-funcall
3726 'tramp-ssh-controlmaster-options v)))
3727 spec)
3728
3729 ;; Replace `login-args' place holders.
3730 (setq
3731 spec (format-spec-make ?t tmpfile)
3732 options (format-spec (or options "") spec)
3733 spec (format-spec-make
3734 ?h (or host "") ?u (or user "") ?p (or port "")
3735 ?c options ?l "")
3736 ;; Add arguments for asynchronous processes.
3737 login-args (append async-args login-args)
3738 ;; Expand format spec.
3739 login-args
3740 (tramp-compat-flatten-tree
3741 (mapcar
3742 (lambda (x)
3743 (setq x (mapcar (lambda (y) (format-spec y spec)) x))
3744 (unless (member "" x) x))
3745 login-args))
3746 ;; Split ControlMaster options.
3747 login-args
3748 (tramp-compat-flatten-tree
3749 (mapcar (lambda (x) (split-string x " ")) login-args))
3750 p (apply
3751 #'start-process
3752 name buffer login-program (append login-args command)))
3753
3754 (tramp-message v 6 "%s" (string-join (process-command p) " "))
3755 ;; Set sentinel and filter.
3756 (when sentinel
3757 (set-process-sentinel p sentinel))
3758 (when filter
3759 (set-process-filter p filter))
3760 ;; Set query flag and process marker for this
3761 ;; process. We ignore errors, because the
3762 ;; process could have finished already.
3763 (ignore-errors
3764 (set-process-query-on-exit-flag p (null noquery))
3765 (set-marker (process-mark p) (point)))
3766 ;; We must flush them here already; otherwise
3767 ;; `rename-file', `delete-file' or
3768 ;; `insert-file-contents' will fail.
3769 (tramp-flush-connection-property v "process-name")
3770 (tramp-flush-connection-property v "process-buffer")
3771 ;; Return process.
3772 p)
3773
3774 ;; Save exit.
3775 (if (string-match-p tramp-temp-buffer-name (buffer-name))
3776 (ignore-errors
3777 (set-process-buffer p nil)
3778 (kill-buffer (current-buffer)))
3779 (set-buffer-modified-p bmp))
3780 (tramp-flush-connection-property v "process-name")
3781 (tramp-flush-connection-property v "process-buffer"))))))))
3643 3782
3644(defun tramp-handle-make-symbolic-link 3783(defun tramp-handle-make-symbolic-link
3645 (target linkname &optional ok-if-already-exists) 3784 (target linkname &optional ok-if-already-exists)
@@ -3676,8 +3815,8 @@ support symbolic links."
3676 (current-buffer)) 3815 (current-buffer))
3677 (t (get-buffer-create 3816 (t (get-buffer-create
3678 (if asynchronous 3817 (if asynchronous
3679 "*Async Shell Command*" 3818 shell-command-buffer-name-async
3680 "*Shell Command Output*"))))) 3819 shell-command-buffer-name)))))
3681 (error-buffer 3820 (error-buffer
3682 (cond 3821 (cond
3683 ((bufferp error-buffer) error-buffer) 3822 ((bufferp error-buffer) error-buffer)
@@ -4706,7 +4845,7 @@ This handles also chrooted environments, which are not regarded as local."
4706 ;; The method shall be applied to one of the shell file name 4845 ;; The method shall be applied to one of the shell file name
4707 ;; handlers. `tramp-local-host-p' is also called for "smb" and 4846 ;; handlers. `tramp-local-host-p' is also called for "smb" and
4708 ;; alike, where it must fail. 4847 ;; alike, where it must fail.
4709 (tramp-get-method-parameter vec 'tramp-login-program) 4848 (tramp-sh-file-name-handler-p vec)
4710 ;; Direct actions aren't possible for crypted directories. 4849 ;; Direct actions aren't possible for crypted directories.
4711 (null tramp-crypt-enabled) 4850 (null tramp-crypt-enabled)
4712 ;; The local temp directory must be writable for the other user. 4851 ;; The local temp directory must be writable for the other user.
diff --git a/lisp/outline.el b/lisp/outline.el
index 28ea8a86e6f..6158ed594e9 100644
--- a/lisp/outline.el
+++ b/lisp/outline.el
@@ -289,12 +289,19 @@ Turning on outline mode calls the value of `text-mode-hook' and then of
289 (list (list nil (concat "^\\(?:" outline-regexp "\\).*$") 0))) 289 (list (list nil (concat "^\\(?:" outline-regexp "\\).*$") 0)))
290 (add-hook 'change-major-mode-hook 'outline-show-all nil t)) 290 (add-hook 'change-major-mode-hook 'outline-show-all nil t))
291 291
292(defvar outline-minor-mode-map)
293
292(defcustom outline-minor-mode-prefix "\C-c@" 294(defcustom outline-minor-mode-prefix "\C-c@"
293 "Prefix key to use for Outline commands in Outline minor mode. 295 "Prefix key to use for Outline commands in Outline minor mode.
294The value of this variable is checked as part of loading Outline mode. 296The value of this variable is checked as part of loading Outline mode.
295After that, changing the prefix key requires manipulating keymaps." 297After that, changing the prefix key requires manipulating keymaps."
296 :type 'string 298 :type 'key-sequence
297 :group 'outlines) 299 :group 'outlines
300 :initialize 'custom-initialize-default
301 :set (lambda (sym val)
302 (define-key outline-minor-mode-map outline-minor-mode-prefix nil)
303 (define-key outline-minor-mode-map val outline-mode-prefix-map)
304 (set-default sym val)))
298 305
299;;;###autoload 306;;;###autoload
300(define-minor-mode outline-minor-mode 307(define-minor-mode outline-minor-mode
diff --git a/lisp/play/snake.el b/lisp/play/snake.el
index d7c0683a05f..70d80c464fc 100644
--- a/lisp/play/snake.el
+++ b/lisp/play/snake.el
@@ -192,6 +192,7 @@ and then start moving it leftwards.")
192(defvar snake-null-map 192(defvar snake-null-map
193 (let ((map (make-sparse-keymap 'snake-null-map))) 193 (let ((map (make-sparse-keymap 'snake-null-map)))
194 (define-key map "n" 'snake-start-game) 194 (define-key map "n" 'snake-start-game)
195 (define-key map "q" 'quit-window)
195 map) 196 map)
196 "Keymap for finished Snake games.") 197 "Keymap for finished Snake games.")
197 198
diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el
index cdbb59a5add..6122caf5189 100644
--- a/lisp/progmodes/cperl-mode.el
+++ b/lisp/progmodes/cperl-mode.el
@@ -3560,19 +3560,18 @@ the sections using `cperl-pod-head-face', `cperl-pod-face',
3560 "\\(\\`\n?\\|^\n\\)=" ; POD 3560 "\\(\\`\n?\\|^\n\\)=" ; POD
3561 "\\|" 3561 "\\|"
3562 ;; One extra () before this: 3562 ;; One extra () before this:
3563 "<<~?" ; HERE-DOC 3563 "<<\\(~?\\)" ; HERE-DOC, indented-p = capture 2
3564 "\\(" ; 1 + 1 3564 "\\(" ; 2 + 1
3565 ;; First variant "BLAH" or just ``. 3565 ;; First variant "BLAH" or just ``.
3566 "[ \t]*" ; Yes, whitespace is allowed! 3566 "[ \t]*" ; Yes, whitespace is allowed!
3567 "\\([\"'`]\\)" ; 2 + 1 = 3 3567 "\\([\"'`]\\)" ; 3 + 1 = 4
3568 "\\([^\"'`\n]*\\)" ; 3 + 1 3568 "\\([^\"'`\n]*\\)" ; 4 + 1
3569 "\\3" 3569 "\\4"
3570 "\\|" 3570 "\\|"
3571 ;; Second variant: Identifier or \ID (same as 'ID') or empty 3571 ;; Second variant: Identifier or \ID (same as 'ID') or empty
3572 "\\\\?\\(\\([a-zA-Z_][a-zA-Z_0-9]*\\)?\\)" ; 4 + 1, 5 + 1 3572 "\\\\?\\(\\([a-zA-Z_][a-zA-Z_0-9]*\\)?\\)" ; 5 + 1, 6 + 1
3573 ;; Do not have <<= or << 30 or <<30 or << $blah. 3573 ;; Do not have <<= or << 30 or <<30 or << $blah.
3574 ;; "\\([^= \t0-9$@%&]\\|[ \t]+[^ \t\n0-9$@%&]\\)" ; 6 + 1 3574 ;; "\\([^= \t0-9$@%&]\\|[ \t]+[^ \t\n0-9$@%&]\\)" ; 6 + 1
3575 "\\(\\)" ; To preserve count of pars :-( 6 + 1
3576 "\\)" 3575 "\\)"
3577 "\\|" 3576 "\\|"
3578 ;; 1+6 extra () before this: 3577 ;; 1+6 extra () before this:
@@ -3762,11 +3761,11 @@ the sections using `cperl-pod-head-face', `cperl-pod-face',
3762 ;; ;; "\\([^= \t0-9$@%&]\\|[ \t]+[^ \t\n0-9$@%&]\\)" ; 6 + 1 3761 ;; ;; "\\([^= \t0-9$@%&]\\|[ \t]+[^ \t\n0-9$@%&]\\)" ; 6 + 1
3763 ;; "\\(\\)" ; To preserve count of pars :-( 6 + 1 3762 ;; "\\(\\)" ; To preserve count of pars :-( 6 + 1
3764 ;; "\\)" 3763 ;; "\\)"
3765 ((match-beginning 2) ; 1 + 1 3764 ((match-beginning 3) ; 2 + 1
3766 (setq b (point) 3765 (setq b (point)
3767 tb (match-beginning 0) 3766 tb (match-beginning 0)
3768 c (and ; not HERE-DOC 3767 c (and ; not HERE-DOC
3769 (match-beginning 5) 3768 (match-beginning 6)
3770 (save-match-data 3769 (save-match-data
3771 (or (looking-at "[ \t]*(") ; << function_call() 3770 (or (looking-at "[ \t]*(") ; << function_call()
3772 (save-excursion ; 1 << func_name, or $foo << 10 3771 (save-excursion ; 1 << func_name, or $foo << 10
@@ -3793,17 +3792,17 @@ the sections using `cperl-pod-head-face', `cperl-pod-face',
3793 (looking-at "\\(printf?\\|say\\|system\\|exec\\|sort\\)\\>"))) 3792 (looking-at "\\(printf?\\|say\\|system\\|exec\\|sort\\)\\>")))
3794 (error t))))))) 3793 (error t)))))))
3795 (error nil))) ; func(<<EOF) 3794 (error nil))) ; func(<<EOF)
3796 (and (not (match-beginning 6)) ; Empty 3795 (and (not (match-beginning 7)) ; Empty
3797 (looking-at 3796 (looking-at
3798 "[ \t]*[=0-9$@%&(]")))))) 3797 "[ \t]*[=0-9$@%&(]"))))))
3799 (if c ; Not here-doc 3798 (if c ; Not here-doc
3800 nil ; Skip it. 3799 nil ; Skip it.
3801 (setq c (match-end 2)) ; 1 + 1 3800 (setq c (match-end 3)) ; 2 + 1
3802 (if (match-beginning 5) ;4 + 1 3801 (if (match-beginning 6) ;6 + 1
3803 (setq b1 (match-beginning 5) ; 4 + 1 3802 (setq b1 (match-beginning 6) ; 5 + 1
3804 e1 (match-end 5)) ; 4 + 1 3803 e1 (match-end 6)) ; 5 + 1
3805 (setq b1 (match-beginning 4) ; 3 + 1 3804 (setq b1 (match-beginning 5) ; 4 + 1
3806 e1 (match-end 4))) ; 3 + 1 3805 e1 (match-end 5))) ; 4 + 1
3807 (setq tag (buffer-substring b1 e1) 3806 (setq tag (buffer-substring b1 e1)
3808 qtag (regexp-quote tag)) 3807 qtag (regexp-quote tag))
3809 (cond (cperl-pod-here-fontify 3808 (cond (cperl-pod-here-fontify
@@ -3818,8 +3817,10 @@ the sections using `cperl-pod-head-face', `cperl-pod-face',
3818 (setq b (point)) 3817 (setq b (point))
3819 ;; We do not search to max, since we may be called from 3818 ;; We do not search to max, since we may be called from
3820 ;; some hook of fontification, and max is random 3819 ;; some hook of fontification, and max is random
3821 (or (and (re-search-forward (concat "^[ \t]*" qtag "$") 3820 (or (and (re-search-forward
3822 stop-point 'toend) 3821 (concat "^" (when (equal (match-string 2) "~") "[ \t]*")
3822 qtag "$")
3823 stop-point 'toend)
3823 ;;;(eq (following-char) ?\n) ; XXXX WHY??? 3824 ;;;(eq (following-char) ?\n) ; XXXX WHY???
3824 ) 3825 )
3825 (progn ; Pretend we matched at the end 3826 (progn ; Pretend we matched at the end
@@ -5752,7 +5753,7 @@ indentation and initial hashes. Behaves usually outside of comment."
5752 (if (eq (char-after (match-beginning 2)) ?%) 5753 (if (eq (char-after (match-beginning 2)) ?%)
5753 'cperl-hash-face 5754 'cperl-hash-face
5754 'cperl-array-face) 5755 'cperl-array-face)
5755 t) ; arrays and hashes 5756 nil) ; arrays and hashes
5756 ("\\(\\([$@]+\\)[a-zA-Z_:][a-zA-Z0-9_:]*\\)[ \t]*\\([[{]\\)" 5757 ("\\(\\([$@]+\\)[a-zA-Z_:][a-zA-Z0-9_:]*\\)[ \t]*\\([[{]\\)"
5757 1 5758 1
5758 (if (= (- (match-end 2) (match-beginning 2)) 1) 5759 (if (= (- (match-end 2) (match-beginning 2)) 1)
@@ -6499,9 +6500,10 @@ If optional argument ALL is `recursive', will process Perl files
6499in subdirectories too." 6500in subdirectories too."
6500 (interactive) 6501 (interactive)
6501 (let ((cmd "etags") 6502 (let ((cmd "etags")
6502 (args '("-l" "none" "-r" 6503 (args `("-l" "none" "-r"
6503 ;; 1=fullname 2=package? 3=name 4=proto? 5=attrs? (VERY APPROX!) 6504 ;; 1=fullname 2=package? 3=name 4=proto? 5=attrs? (VERY APPROX!)
6504 "/\\<" cperl-sub-regexp "[ \\t]+\\(\\([a-zA-Z0-9:_]*::\\)?\\([a-zA-Z0-9_]+\\)\\)[ \\t]*\\(([^()]*)[ \t]*\\)?\\([ \t]*:[^#{;]*\\)?\\([{#]\\|$\\)/\\3/" 6505 ,(concat
6506 "/\\<" cperl-sub-regexp "[ \\t]+\\(\\([a-zA-Z0-9:_]*::\\)?\\([a-zA-Z0-9_]+\\)\\)[ \\t]*\\(([^()]*)[ \t]*\\)?\\([ \t]*:[^#{;]*\\)?\\([{#]\\|$\\)/\\3/")
6505 "-r" 6507 "-r"
6506 "/\\<package[ \\t]+\\(\\([a-zA-Z0-9:_]*::\\)?\\([a-zA-Z0-9_]+\\)\\)[ \\t]*\\([#;]\\|$\\)/\\1/" 6508 "/\\<package[ \\t]+\\(\\([a-zA-Z0-9:_]*::\\)?\\([a-zA-Z0-9_]+\\)\\)[ \\t]*\\([#;]\\|$\\)/\\1/"
6507 "-r" 6509 "-r"
@@ -6786,6 +6788,7 @@ Use as
6786 (or topdir 6788 (or topdir
6787 (setq topdir default-directory)) 6789 (setq topdir default-directory))
6788 (let ((tags-file-name "TAGS") 6790 (let ((tags-file-name "TAGS")
6791 (inhibit-read-only t)
6789 (case-fold-search nil) 6792 (case-fold-search nil)
6790 xs rel) 6793 xs rel)
6791 (save-excursion 6794 (save-excursion
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index 51b9347bb93..b6161351f0b 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -1166,7 +1166,9 @@ Save the result in `project-list-file' if the list of projects has changed."
1166 (project--ensure-read-project-list) 1166 (project--ensure-read-project-list)
1167 (let ((dir (project-root pr))) 1167 (let ((dir (project-root pr)))
1168 (unless (equal (caar project--list) dir) 1168 (unless (equal (caar project--list) dir)
1169 (setq project--list (assoc-delete-all dir project--list)) 1169 (dolist (ent project--list)
1170 (when (equal dir (car ent))
1171 (setq project--list (delq ent project--list))))
1170 (push (list dir) project--list) 1172 (push (list dir) project--list)
1171 (project--write-project-list)))) 1173 (project--write-project-list))))
1172 1174
@@ -1176,8 +1178,8 @@ If the directory was in the list before the removal, save the
1176result in `project-list-file'. Announce the project's removal 1178result in `project-list-file'. Announce the project's removal
1177from the list." 1179from the list."
1178 (project--ensure-read-project-list) 1180 (project--ensure-read-project-list)
1179 (when (assoc pr-dir project--list) 1181 (when-let ((ent (assoc pr-dir project--list)))
1180 (setq project--list (assoc-delete-all pr-dir project--list)) 1182 (setq project--list (delq ent project--list))
1181 (message "Project `%s' not found; removed from list" pr-dir) 1183 (message "Project `%s' not found; removed from list" pr-dir)
1182 (project--write-project-list))) 1184 (project--write-project-list)))
1183 1185
diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el
index 044d7820ee3..5a47594878e 100644
--- a/lisp/progmodes/sh-script.el
+++ b/lisp/progmodes/sh-script.el
@@ -838,7 +838,7 @@ See `sh-feature'.")
838 font-lock-variable-name-face)) 838 font-lock-variable-name-face))
839 839
840 (rc sh-append es) 840 (rc sh-append es)
841 (bash sh-append sh ("\\$(\\(\\sw+\\)" (1 'sh-quoted-exec t) )) 841 (bash sh-append sh ("\\$(\\([^)\n]+\\)" (1 'sh-quoted-exec t) ))
842 (sh sh-append shell 842 (sh sh-append shell
843 ;; Variable names. 843 ;; Variable names.
844 ("\\$\\({#?\\)?\\([[:alpha:]_][[:alnum:]_]*\\|[-#?@!]\\)" 2 844 ("\\$\\({#?\\)?\\([[:alpha:]_][[:alnum:]_]*\\|[-#?@!]\\)" 2
diff --git a/lisp/progmodes/sql.el b/lisp/progmodes/sql.el
index c86fc59ac16..a70b5ed60d6 100644
--- a/lisp/progmodes/sql.el
+++ b/lisp/progmodes/sql.el
@@ -1508,6 +1508,22 @@ Based on `comint-mode-map'.")
1508 table) 1508 table)
1509 "Syntax table used in `sql-mode' and `sql-interactive-mode'.") 1509 "Syntax table used in `sql-mode' and `sql-interactive-mode'.")
1510 1510
1511;;; Syntax Properties
1512
1513;; `sql--syntax-propertize-escaped-apostrophe', as follows, was
1514;; (analysed and) adapted from `pascal--syntax-propertize' in
1515;; pascal.el because basic syntax parsing cannot handle the SQL ''
1516;; construct within strings.
1517
1518(defconst sql--syntax-propertize-escaped-apostrophe
1519 (syntax-propertize-rules
1520 ("''"
1521 (0
1522 (if (save-excursion (nth 3 (syntax-ppss (match-beginning 0))))
1523 (string-to-syntax ".")
1524 (forward-char -1)
1525 nil)))))
1526
1511;; Font lock support 1527;; Font lock support
1512 1528
1513(defvar sql-mode-font-lock-object-name 1529(defvar sql-mode-font-lock-object-name
@@ -4210,6 +4226,10 @@ must tell Emacs. Here's how to do that in your init file:
4210 (setq-local abbrev-all-caps 1) 4226 (setq-local abbrev-all-caps 1)
4211 ;; Contains the name of database objects 4227 ;; Contains the name of database objects
4212 (set (make-local-variable 'sql-contains-names) t) 4228 (set (make-local-variable 'sql-contains-names) t)
4229 ;; Activate punctuation syntax table property for
4230 ;; escaped apostrophes within strings:
4231 (setq-local syntax-propertize-function
4232 sql--syntax-propertize-escaped-apostrophe)
4213 ;; Set syntax and font-face highlighting 4233 ;; Set syntax and font-face highlighting
4214 ;; Catch changes to sql-product and highlight accordingly 4234 ;; Catch changes to sql-product and highlight accordingly
4215 (sql-set-product (or sql-product 'ansi)) ; Fixes bug#13591 4235 (sql-set-product (or sql-product 'ansi)) ; Fixes bug#13591
diff --git a/lisp/recentf.el b/lisp/recentf.el
index 27918a9739c..877edd4be1f 100644
--- a/lisp/recentf.el
+++ b/lisp/recentf.el
@@ -1289,7 +1289,8 @@ Write data into the file specified by `recentf-save-file'."
1289 (insert "\n \n;; Local Variables:\n" 1289 (insert "\n \n;; Local Variables:\n"
1290 (format ";; coding: %s\n" recentf-save-file-coding-system) 1290 (format ";; coding: %s\n" recentf-save-file-coding-system)
1291 ";; End:\n") 1291 ";; End:\n")
1292 (write-file (expand-file-name recentf-save-file)) 1292 (write-region (point-min) (point-max)
1293 (expand-file-name recentf-save-file))
1293 (when recentf-save-file-modes 1294 (when recentf-save-file-modes
1294 (set-file-modes recentf-save-file recentf-save-file-modes)) 1295 (set-file-modes recentf-save-file recentf-save-file-modes))
1295 nil) 1296 nil)
diff --git a/lisp/saveplace.el b/lisp/saveplace.el
index 46738ab03dc..d420bfb4e9f 100644
--- a/lisp/saveplace.el
+++ b/lisp/saveplace.el
@@ -1,4 +1,4 @@
1;;; saveplace.el --- automatically save place in files 1;;; saveplace.el --- automatically save place in files -*- lexical-binding:t -*-
2 2
3;; Copyright (C) 1993-1994, 2001-2020 Free Software Foundation, Inc. 3;; Copyright (C) 1993-1994, 2001-2020 Free Software Foundation, Inc.
4 4
@@ -42,7 +42,6 @@
42 "Automatically save place in files." 42 "Automatically save place in files."
43 :group 'data) 43 :group 'data)
44 44
45
46(defvar save-place-alist nil 45(defvar save-place-alist nil
47 "Alist of saved places to go back to when revisiting files. 46 "Alist of saved places to go back to when revisiting files.
48Each element looks like (FILENAME . POSITION); 47Each element looks like (FILENAME . POSITION);
@@ -175,10 +174,11 @@ file:
175(declare-function dired-get-filename "dired" (&optional localp no-error-if-not-filep)) 174(declare-function dired-get-filename "dired" (&optional localp no-error-if-not-filep))
176 175
177(defun save-place-to-alist () 176(defun save-place-to-alist ()
178 ;; put filename and point in a cons box and then cons that onto the 177 "Add current buffer filename and position to `save-place-alist'.
179 ;; front of the save-place-alist, if save-place-mode is non-nil. 178Put filename and point in a cons box and then cons that onto the
180 ;; Otherwise, just delete that file from the alist. 179front of the `save-place-alist', if `save-place-mode' is non-nil.
181 ;; first check to make sure alist has been loaded in from the master 180Otherwise, just delete that file from the alist."
181 ;; First check to make sure alist has been loaded in from the master
182 ;; file. If not, do so, then feel free to modify the alist. It 182 ;; file. If not, do so, then feel free to modify the alist. It
183 ;; will be saved again when Emacs is killed. 183 ;; will be saved again when Emacs is killed.
184 (or save-place-loaded (load-save-place-alist-from-file)) 184 (or save-place-loaded (load-save-place-alist-from-file))
diff --git a/lisp/scroll-lock.el b/lisp/scroll-lock.el
index 3a6d9d36429..f20ea1bcc87 100644
--- a/lisp/scroll-lock.el
+++ b/lisp/scroll-lock.el
@@ -1,4 +1,4 @@
1;;; scroll-lock.el --- Scroll lock scrolling. 1;;; scroll-lock.el --- Scroll lock scrolling. -*- lexical-binding:t -*-
2 2
3;; Copyright (C) 2005-2020 Free Software Foundation, Inc. 3;; Copyright (C) 2005-2020 Free Software Foundation, Inc.
4 4
diff --git a/lisp/simple.el b/lisp/simple.el
index 2f92238e640..6c9584aaa39 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -3369,6 +3369,14 @@ which is defined in the `warnings' library.\n")
3369 (setq buffer-undo-list nil) 3369 (setq buffer-undo-list nil)
3370 t)) 3370 t))
3371 3371
3372;;;; Shell commands
3373
3374(defconst shell-command-buffer-name "*Shell Command Output*"
3375 "Name of the output buffer for shell commands.")
3376
3377(defconst shell-command-buffer-name-async "*Async Shell Command*"
3378 "Name of the output buffer for asynchronous shell commands.")
3379
3372(defvar shell-command-history nil 3380(defvar shell-command-history nil
3373 "History list for some commands that read shell commands. 3381 "History list for some commands that read shell commands.
3374 3382
@@ -3433,7 +3441,7 @@ to `shell-command-history'."
3433(defcustom async-shell-command-buffer 'confirm-new-buffer 3441(defcustom async-shell-command-buffer 'confirm-new-buffer
3434 "What to do when the output buffer is used by another shell command. 3442 "What to do when the output buffer is used by another shell command.
3435This option specifies how to resolve the conflict where a new command 3443This option specifies how to resolve the conflict where a new command
3436wants to direct its output to the buffer `*Async Shell Command*', 3444wants to direct its output to the buffer `shell-command-buffer-name-async',
3437but this buffer is already taken by another running shell command. 3445but this buffer is already taken by another running shell command.
3438 3446
3439The value `confirm-kill-process' is used to ask for confirmation before 3447The value `confirm-kill-process' is used to ask for confirmation before
@@ -3585,14 +3593,14 @@ whose `car' is BUFFER."
3585Like `shell-command', but adds `&' at the end of COMMAND 3593Like `shell-command', but adds `&' at the end of COMMAND
3586to execute it asynchronously. 3594to execute it asynchronously.
3587 3595
3588The output appears in the buffer `*Async Shell Command*'. 3596The output appears in the buffer `shell-command-buffer-name-async'.
3589That buffer is in shell mode. 3597That buffer is in shell mode.
3590 3598
3591You can configure `async-shell-command-buffer' to specify what to do 3599You can configure `async-shell-command-buffer' to specify what to do
3592when the `*Async Shell Command*' buffer is already taken by another 3600when the `shell-command-buffer-name-async' buffer is already taken by another
3593running shell command. To run COMMAND without displaying the output 3601running shell command. To run COMMAND without displaying the output
3594in a window you can configure `display-buffer-alist' to use the action 3602in a window you can configure `display-buffer-alist' to use the action
3595`display-buffer-no-window' for the buffer `*Async Shell Command*'. 3603`display-buffer-no-window' for the buffer `shell-command-buffer-name-async'.
3596 3604
3597In Elisp, you will often be better served by calling `start-process' 3605In Elisp, you will often be better served by calling `start-process'
3598directly, since it offers more control and does not impose the use of 3606directly, since it offers more control and does not impose the use of
@@ -3628,12 +3636,12 @@ If `shell-command-prompt-show-cwd' is non-nil, show the current
3628directory in the prompt. 3636directory in the prompt.
3629 3637
3630If COMMAND ends in `&', execute it asynchronously. 3638If COMMAND ends in `&', execute it asynchronously.
3631The output appears in the buffer `*Async Shell Command*'. 3639The output appears in the buffer `shell-command-buffer-name-async'.
3632That buffer is in shell mode. You can also use 3640That buffer is in shell mode. You can also use
3633`async-shell-command' that automatically adds `&'. 3641`async-shell-command' that automatically adds `&'.
3634 3642
3635Otherwise, COMMAND is executed synchronously. The output appears in 3643Otherwise, COMMAND is executed synchronously. The output appears in
3636the buffer `*Shell Command Output*'. If the output is short enough to 3644the buffer `shell-command-buffer-name'. If the output is short enough to
3637display in the echo area (which is determined by the variables 3645display in the echo area (which is determined by the variables
3638`resize-mini-windows' and `max-mini-window-height'), it is shown 3646`resize-mini-windows' and `max-mini-window-height'), it is shown
3639there, but it is nonetheless available in buffer `*Shell Command 3647there, but it is nonetheless available in buffer `*Shell Command
@@ -3756,7 +3764,7 @@ impose the use of a shell (with its need to quote arguments)."
3756 (if (string-match "[ \t]*&[ \t]*\\'" command) 3764 (if (string-match "[ \t]*&[ \t]*\\'" command)
3757 ;; Command ending with ampersand means asynchronous. 3765 ;; Command ending with ampersand means asynchronous.
3758 (let* ((buffer (get-buffer-create 3766 (let* ((buffer (get-buffer-create
3759 (or output-buffer "*Async Shell Command*"))) 3767 (or output-buffer shell-command-buffer-name-async)))
3760 (bname (buffer-name buffer)) 3768 (bname (buffer-name buffer))
3761 (proc (get-buffer-process buffer)) 3769 (proc (get-buffer-process buffer))
3762 (directory default-directory)) 3770 (directory default-directory))
@@ -3908,7 +3916,7 @@ and are used only if a pop-up buffer is displayed."
3908 error-buffer display-error-buffer 3916 error-buffer display-error-buffer
3909 region-noncontiguous-p) 3917 region-noncontiguous-p)
3910 "Execute string COMMAND in inferior shell with region as input. 3918 "Execute string COMMAND in inferior shell with region as input.
3911Normally display output (if any) in temp buffer `*Shell Command Output*'; 3919Normally display output (if any) in temp buffer `shell-command-buffer-name';
3912Prefix arg means replace the region with it. Return the exit code of 3920Prefix arg means replace the region with it. Return the exit code of
3913COMMAND. 3921COMMAND.
3914 3922
@@ -3927,7 +3935,7 @@ in the echo area or in a buffer.
3927If the output is short enough to display in the echo area 3935If the output is short enough to display in the echo area
3928\(determined by the variable `max-mini-window-height' if 3936\(determined by the variable `max-mini-window-height' if
3929`resize-mini-windows' is non-nil), it is shown there. 3937`resize-mini-windows' is non-nil), it is shown there.
3930Otherwise it is displayed in the buffer `*Shell Command Output*'. 3938Otherwise it is displayed in the buffer `shell-command-buffer-name'.
3931The output is available in that buffer in both cases. 3939The output is available in that buffer in both cases.
3932 3940
3933If there is output and an error, a message about the error 3941If there is output and an error, a message about the error
@@ -3937,7 +3945,7 @@ Optional fourth arg OUTPUT-BUFFER specifies where to put the
3937command's output. If the value is a buffer or buffer name, 3945command's output. If the value is a buffer or buffer name,
3938erase that buffer and insert the output there; a non-nil value of 3946erase that buffer and insert the output there; a non-nil value of
3939`shell-command-dont-erase-buffer' prevent to erase the buffer. 3947`shell-command-dont-erase-buffer' prevent to erase the buffer.
3940If the value is nil, use the buffer `*Shell Command Output*'. 3948If the value is nil, use the buffer `shell-command-buffer-name'.
3941Any other non-nil value means to insert the output in the 3949Any other non-nil value means to insert the output in the
3942current buffer after START. 3950current buffer after START.
3943 3951
@@ -4006,7 +4014,7 @@ characters."
4006 (funcall region-insert-function output)) 4014 (funcall region-insert-function output))
4007 (t 4015 (t
4008 (let ((buffer (get-buffer-create 4016 (let ((buffer (get-buffer-create
4009 (or output-buffer "*Shell Command Output*")))) 4017 (or output-buffer shell-command-buffer-name))))
4010 (with-current-buffer buffer 4018 (with-current-buffer buffer
4011 (erase-buffer) 4019 (erase-buffer)
4012 (funcall region-insert-function output)) 4020 (funcall region-insert-function output))
@@ -4025,7 +4033,7 @@ characters."
4025 (list t error-file) 4033 (list t error-file)
4026 t))) 4034 t)))
4027 ;; It is rude to delete a buffer that the command is not using. 4035 ;; It is rude to delete a buffer that the command is not using.
4028 ;; (let ((shell-buffer (get-buffer "*Shell Command Output*"))) 4036 ;; (let ((shell-buffer (get-buffer shell-command-buffer-name)))
4029 ;; (and shell-buffer (not (eq shell-buffer (current-buffer))) 4037 ;; (and shell-buffer (not (eq shell-buffer (current-buffer)))
4030 ;; (kill-buffer shell-buffer))) 4038 ;; (kill-buffer shell-buffer)))
4031 ;; Don't muck with mark unless REPLACE says we should. 4039 ;; Don't muck with mark unless REPLACE says we should.
@@ -4033,12 +4041,13 @@ characters."
4033 ;; No prefix argument: put the output in a temp buffer, 4041 ;; No prefix argument: put the output in a temp buffer,
4034 ;; replacing its entire contents. 4042 ;; replacing its entire contents.
4035 (let ((buffer (get-buffer-create 4043 (let ((buffer (get-buffer-create
4036 (or output-buffer "*Shell Command Output*")))) 4044 (or output-buffer shell-command-buffer-name))))
4037 (set-buffer-major-mode buffer) ; Enable globalized modes (bug#38111) 4045 (set-buffer-major-mode buffer) ; Enable globalized modes (bug#38111)
4038 (unwind-protect 4046 (unwind-protect
4039 (if (and (eq buffer (current-buffer)) 4047 (if (and (eq buffer (current-buffer))
4040 (or (memq shell-command-dont-erase-buffer '(nil erase)) 4048 (or (memq shell-command-dont-erase-buffer '(nil erase))
4041 (and (not (eq buffer (get-buffer "*Shell Command Output*"))) 4049 (and (not (eq buffer (get-buffer
4050 shell-command-buffer-name)))
4042 (not (region-active-p))))) 4051 (not (region-active-p)))))
4043 ;; If the input is the same buffer as the output, 4052 ;; If the input is the same buffer as the output,
4044 ;; delete everything but the specified region, 4053 ;; delete everything but the specified region,
diff --git a/lisp/skeleton.el b/lisp/skeleton.el
index 8c694c128b5..ea4e5dbc227 100644
--- a/lisp/skeleton.el
+++ b/lisp/skeleton.el
@@ -1,4 +1,4 @@
1;;; skeleton.el --- Lisp language extension for writing statement skeletons 1;;; skeleton.el --- Lisp language extension for writing statement skeletons -*- lexical-binding: t; -*-
2 2
3;; Copyright (C) 1993-1996, 2001-2020 Free Software Foundation, Inc. 3;; Copyright (C) 1993-1996, 2001-2020 Free Software Foundation, Inc.
4 4
@@ -135,7 +135,8 @@ A prefix argument of -1 says to wrap around region, even if not highlighted.
135A prefix argument of zero says to wrap around zero words---that is, nothing. 135A prefix argument of zero says to wrap around zero words---that is, nothing.
136This is a way of overriding the use of a highlighted region.") 136This is a way of overriding the use of a highlighted region.")
137 (interactive "*P\nP") 137 (interactive "*P\nP")
138 (skeleton-proxy-new ',skeleton str arg)))) 138 (atomic-change-group
139 (skeleton-proxy-new ',skeleton str arg)))))
139 140
140;;;###autoload 141;;;###autoload
141(defun skeleton-proxy-new (skeleton &optional str arg) 142(defun skeleton-proxy-new (skeleton &optional str arg)
@@ -154,8 +155,7 @@ of `str' whereas the skeleton's interactor is then ignored."
154 (prefix-numeric-value (or arg 155 (prefix-numeric-value (or arg
155 current-prefix-arg)) 156 current-prefix-arg))
156 (and skeleton-autowrap 157 (and skeleton-autowrap
157 (or (eq last-command 'mouse-drag-region) 158 (use-region-p)
158 (and transient-mark-mode mark-active))
159 ;; Deactivate the mark, in case one of the 159 ;; Deactivate the mark, in case one of the
160 ;; elements of the skeleton is sensitive 160 ;; elements of the skeleton is sensitive
161 ;; to such situations (e.g. it is itself a 161 ;; to such situations (e.g. it is itself a
@@ -258,23 +258,25 @@ available:
258 (goto-char (car skeleton-regions)) 258 (goto-char (car skeleton-regions))
259 (setq skeleton-regions (cdr skeleton-regions))) 259 (setq skeleton-regions (cdr skeleton-regions)))
260 (let ((beg (point)) 260 (let ((beg (point))
261 skeleton-modified skeleton-point resume: help input v1 v2) 261 skeleton-modified skeleton-point) ;; resume:
262 (setq skeleton-positions nil) 262 (with-suppressed-warnings ((lexical help input v1 v2))
263 (unwind-protect 263 (dlet (help input v1 v2)
264 (cl-progv 264 (setq skeleton-positions nil)
265 (mapcar #'car skeleton-further-elements) 265 (unwind-protect
266 (mapcar (lambda (x) (eval (cadr x))) skeleton-further-elements) 266 (cl-progv
267 (skeleton-internal-list skeleton str)) 267 (mapcar #'car skeleton-further-elements)
268 (or (eolp) (not skeleton-end-newline) (newline-and-indent)) 268 (mapcar (lambda (x) (eval (cadr x) t)) skeleton-further-elements)
269 (run-hooks 'skeleton-end-hook) 269 (skeleton-internal-list skeleton str))
270 (sit-for 0) 270 (or (eolp) (not skeleton-end-newline) (newline-and-indent))
271 (or (not (eq (window-buffer) (current-buffer))) 271 (run-hooks 'skeleton-end-hook)
272 (pos-visible-in-window-p beg) 272 (sit-for 0)
273 (progn 273 (or (not (eq (window-buffer) (current-buffer)))
274 (goto-char beg) 274 (pos-visible-in-window-p beg)
275 (recenter 0))) 275 (progn
276 (if skeleton-point 276 (goto-char beg)
277 (goto-char skeleton-point)))))) 277 (recenter 0)))
278 (if skeleton-point
279 (goto-char skeleton-point))))))))
278 280
279(defun skeleton-read (prompt &optional initial-input recursive) 281(defun skeleton-read (prompt &optional initial-input recursive)
280 "Function for reading a string from the minibuffer within skeletons. 282 "Function for reading a string from the minibuffer within skeletons.
@@ -327,36 +329,39 @@ automatically, and you are prompted to fill in the variable parts.")))
327 (signal 'quit t) 329 (signal 'quit t)
328 prompt)) 330 prompt))
329 331
330(defun skeleton-internal-list (skeleton-il &optional str recursive) 332(defun skeleton-internal-list (skeleton &optional str recursive)
331 (let* ((start (line-beginning-position)) 333 (let* ((start (line-beginning-position))
332 (column (current-column)) 334 (column (current-column))
333 (line (buffer-substring start (line-end-position))) 335 (line (buffer-substring start (line-end-position)))
334 opoint) 336 (skeleton-il skeleton)
335 (or str 337 opoint)
336 (setq str `(setq str 338 (with-suppressed-warnings ((lexical str))
337 (skeleton-read ',(car skeleton-il) nil ,recursive)))) 339 (dlet ((str (or str
338 (when (and (eq (cadr skeleton-il) '\n) (not recursive) 340 `(setq str
339 (save-excursion (skip-chars-backward " \t") (bolp))) 341 (skeleton-read ',(car skeleton-il)
340 (setq skeleton-il (cons nil (cons '> (cddr skeleton-il))))) 342 nil ,recursive)))))
341 (while (setq skeleton-modified (eq opoint (point)) 343 (when (and (eq (cadr skeleton-il) '\n) (not recursive)
342 opoint (point) 344 (save-excursion (skip-chars-backward " \t") (bolp)))
343 skeleton-il (cdr skeleton-il)) 345 (setq skeleton-il (cons nil (cons '> (cddr skeleton-il)))))
344 (condition-case quit 346 (while (setq skeleton-modified (eq opoint (point))
345 (skeleton-internal-1 (car skeleton-il) nil recursive) 347 opoint (point)
346 (quit 348 skeleton-il (cdr skeleton-il))
347 (if (eq (cdr quit) 'recursive) 349 (condition-case quit
348 (setq recursive 'quit 350 (skeleton-internal-1 (car skeleton-il) nil recursive)
349 skeleton-il (memq 'resume: skeleton-il)) 351 (quit
350 ;; Remove the subskeleton as far as it has been shown 352 (if (eq (cdr quit) 'recursive)
351 ;; the subskeleton shouldn't have deleted outside current line. 353 (setq recursive 'quit
352 (end-of-line) 354 skeleton-il (memq 'resume: skeleton-il))
353 (delete-region start (point)) 355 ;; Remove the subskeleton as far as it has been shown
354 (insert line) 356 ;; the subskeleton shouldn't have deleted outside current line.
355 (move-to-column column) 357 (end-of-line)
356 (if (cdr quit) 358 (delete-region start (point))
357 (setq skeleton-il () 359 (insert line)
358 recursive nil) 360 (move-to-column column)
359 (signal 'quit 'recursive))))))) 361 (if (cdr quit)
362 (setq skeleton-il ()
363 recursive nil)
364 (signal 'quit 'recursive)))))))))
360 ;; maybe continue loop or go on to next outer resume: section 365 ;; maybe continue loop or go on to next outer resume: section
361 (if (eq recursive 'quit) 366 (if (eq recursive 'quit)
362 (signal 'quit 'recursive) 367 (signal 'quit 'recursive)
diff --git a/lisp/so-long.el b/lisp/so-long.el
index 6b05f4821b1..f2c078ba841 100644
--- a/lisp/so-long.el
+++ b/lisp/so-long.el
@@ -38,7 +38,7 @@
38;; compacted into the smallest file size possible, which often entails removing 38;; compacted into the smallest file size possible, which often entails removing
39;; newlines should they not be strictly necessary). This can result in lines 39;; newlines should they not be strictly necessary). This can result in lines
40;; which are many thousands of characters long, and most programming modes 40;; which are many thousands of characters long, and most programming modes
41;; simply aren't optimized (remotely) for this scenario, so performance can 41;; simply aren't optimised (remotely) for this scenario, so performance can
42;; suffer significantly. 42;; suffer significantly.
43;; 43;;
44;; When such files are detected, the command `so-long' is automatically called, 44;; When such files are detected, the command `so-long' is automatically called,
@@ -69,7 +69,7 @@
69;; the long lines. In such circumstances you may find that `longlines-mode' is 69;; the long lines. In such circumstances you may find that `longlines-mode' is
70;; the most helpful facility. 70;; the most helpful facility.
71;; 71;;
72;; Note also that the mitigation is automatically triggered when visiting a 72;; Note also that the mitigations are automatically triggered when visiting a
73;; file. The library does not automatically detect if long lines are inserted 73;; file. The library does not automatically detect if long lines are inserted
74;; into an existing buffer (although the `so-long' command can be invoked 74;; into an existing buffer (although the `so-long' command can be invoked
75;; manually in such situations). 75;; manually in such situations).
@@ -90,7 +90,7 @@
90;; * Overview of modes and commands 90;; * Overview of modes and commands
91;; -------------------------------- 91;; --------------------------------
92;; - `global-so-long-mode' - A global minor mode which enables the automated 92;; - `global-so-long-mode' - A global minor mode which enables the automated
93;; behavior, causing the user's preferred action to be invoked whenever a 93;; behaviour, causing the user's preferred action to be invoked whenever a
94;; newly-visited file contains excessively long lines. 94;; newly-visited file contains excessively long lines.
95;; - `so-long-mode' - A major mode, and the default action. 95;; - `so-long-mode' - A major mode, and the default action.
96;; - `so-long-minor-mode' - A minor mode version of the major mode, and an 96;; - `so-long-minor-mode' - A minor mode version of the major mode, and an
@@ -111,7 +111,7 @@
111;; 111;;
112;; On rare occasions you may choose to manually invoke the `so-long' command, 112;; On rare occasions you may choose to manually invoke the `so-long' command,
113;; which invokes your preferred `so-long-action' (exactly as the automatic 113;; which invokes your preferred `so-long-action' (exactly as the automatic
114;; behavior would do if it had detected long lines). You might use this if a 114;; behaviour would do if it had detected long lines). You might use this if a
115;; problematic file did not meet your configured criteria, and you wished to 115;; problematic file did not meet your configured criteria, and you wished to
116;; trigger the performance improvements manually. 116;; trigger the performance improvements manually.
117;; 117;;
@@ -120,7 +120,7 @@
120;; available to `so-long' but, like any other mode, they can be invoked directly 120;; available to `so-long' but, like any other mode, they can be invoked directly
121;; if you have a need to do that (see also "Other ways of using so-long" below). 121;; if you have a need to do that (see also "Other ways of using so-long" below).
122;; 122;;
123;; If the behavior ever triggers when you did not want it to, you can use the 123;; If the behaviour ever triggers when you did not want it to, you can use the
124;; `so-long-revert' command to restore the buffer to its original state. 124;; `so-long-revert' command to restore the buffer to its original state.
125 125
126;; * Basic configuration 126;; * Basic configuration
@@ -199,7 +199,7 @@
199;; 199;;
200;; Note that `so-long-minor-modes' is not useful for other global minor modes 200;; Note that `so-long-minor-modes' is not useful for other global minor modes
201;; (as distinguished from globalized minor modes), but in some cases it will be 201;; (as distinguished from globalized minor modes), but in some cases it will be
202;; possible to inhibit or otherwise counter-act the behavior of a global mode 202;; possible to inhibit or otherwise counter-act the behaviour of a global mode
203;; by overriding variables, or by employing hooks (see below). You would need 203;; by overriding variables, or by employing hooks (see below). You would need
204;; to inspect the code for a given global mode (on a case by case basis) to 204;; to inspect the code for a given global mode (on a case by case basis) to
205;; determine whether it's possible to inhibit it for a single buffer -- and if 205;; determine whether it's possible to inhibit it for a single buffer -- and if
@@ -211,7 +211,7 @@
211;; If `so-long-action' is set to either `so-long-mode' or `so-long-minor-mode', 211;; If `so-long-action' is set to either `so-long-mode' or `so-long-minor-mode',
212;; the buffer-local value for each variable in the list is set to the associated 212;; the buffer-local value for each variable in the list is set to the associated
213;; value in the alist. Use this to enforce values which will improve 213;; value in the alist. Use this to enforce values which will improve
214;; performance or otherwise avoid undesirable behaviors. If `so-long-revert' 214;; performance or otherwise avoid undesirable behaviours. If `so-long-revert'
215;; is called, then the original values are restored. 215;; is called, then the original values are restored.
216 216
217;; * Hooks 217;; * Hooks
@@ -325,7 +325,7 @@
325;; meaning you would need to add to `safe-local-variable-values' in order to 325;; meaning you would need to add to `safe-local-variable-values' in order to
326;; avoid being queried about them. 326;; avoid being queried about them.
327;; 327;;
328;; Finally, the `so-long-predicate' user option enables the automated behavior 328;; Finally, the `so-long-predicate' user option enables the automated behaviour
329;; to be determined by a custom function, if greater control is needed. 329;; to be determined by a custom function, if greater control is needed.
330 330
331;; * Implementation notes 331;; * Implementation notes
@@ -342,7 +342,7 @@
342 342
343;; * Caveats 343;; * Caveats
344;; --------- 344;; ---------
345;; The variables affecting the automated behavior of this library (such as 345;; The variables affecting the automated behaviour of this library (such as
346;; `so-long-action') can be used as file- or dir-local values in Emacs 26+, but 346;; `so-long-action') can be used as file- or dir-local values in Emacs 26+, but
347;; not in previous versions of Emacs. This is on account of improvements made 347;; not in previous versions of Emacs. This is on account of improvements made
348;; to `normal-mode' in 26.1, which altered the execution order with respect to 348;; to `normal-mode' in 26.1, which altered the execution order with respect to
@@ -386,7 +386,7 @@
386;; - Added sgml-mode and nxml-mode to `so-long-target-modes'. 386;; - Added sgml-mode and nxml-mode to `so-long-target-modes'.
387;; 0.7.4 - Refactored the handling of `whitespace-mode'. 387;; 0.7.4 - Refactored the handling of `whitespace-mode'.
388;; 0.7.3 - Added customize group `so-long' with user options. 388;; 0.7.3 - Added customize group `so-long' with user options.
389;; - Added `so-long-original-values' to generalize the storage and 389;; - Added `so-long-original-values' to generalise the storage and
390;; restoration of values from the original mode upon `so-long-revert'. 390;; restoration of values from the original mode upon `so-long-revert'.
391;; - Added `so-long-revert-hook'. 391;; - Added `so-long-revert-hook'.
392;; 0.7.2 - Remember the original major mode even with M-x `so-long-mode'. 392;; 0.7.2 - Remember the original major mode even with M-x `so-long-mode'.
@@ -399,7 +399,7 @@
399;; 0.6 - Added `so-long-minor-modes' and `so-long-hook'. 399;; 0.6 - Added `so-long-minor-modes' and `so-long-hook'.
400;; 0.5 - Renamed library to "so-long.el". 400;; 0.5 - Renamed library to "so-long.el".
401;; - Added explicit `so-long-enable' command to activate our advice. 401;; - Added explicit `so-long-enable' command to activate our advice.
402;; 0.4 - Amended/documented behavior with file-local 'mode' variables. 402;; 0.4 - Amended/documented behaviour with file-local 'mode' variables.
403;; 0.3 - Defer to a file-local 'mode' variable. 403;; 0.3 - Defer to a file-local 'mode' variable.
404;; 0.2 - Initial release to EmacsWiki. 404;; 0.2 - Initial release to EmacsWiki.
405;; 0.1 - Experimental. 405;; 0.1 - Experimental.
@@ -421,7 +421,7 @@
421Has no effect if `global-so-long-mode' is not enabled.") 421Has no effect if `global-so-long-mode' is not enabled.")
422 422
423(defvar-local so-long--active nil ; internal use 423(defvar-local so-long--active nil ; internal use
424 "Non-nil when `so-long' mitigation is in effect.") 424 "Non-nil when `so-long' mitigations are in effect.")
425 425
426(defvar so-long--set-auto-mode nil ; internal use 426(defvar so-long--set-auto-mode nil ; internal use
427 "Non-nil while `set-auto-mode' is executing.") 427 "Non-nil while `set-auto-mode' is executing.")
@@ -500,7 +500,7 @@ files would prevent Emacs from handling them correctly."
500(defcustom so-long-invisible-buffer-function #'so-long-deferred 500(defcustom so-long-invisible-buffer-function #'so-long-deferred
501 "Function called in place of `so-long' when the buffer is not displayed. 501 "Function called in place of `so-long' when the buffer is not displayed.
502 502
503This affects the behavior of `global-so-long-mode'. 503This affects the behaviour of `global-so-long-mode'.
504 504
505We treat invisible buffers differently from displayed buffers because, in 505We treat invisible buffers differently from displayed buffers because, in
506cases where a library is using a buffer for behind-the-scenes processing, 506cases where a library is using a buffer for behind-the-scenes processing,
@@ -548,7 +548,7 @@ Defaults to `so-long-detected-long-line-p'."
548(defun so-long--action-type () 548(defun so-long--action-type ()
549 "Generate a :type for `so-long-action' based on `so-long-action-alist'." 549 "Generate a :type for `so-long-action' based on `so-long-action-alist'."
550 ;; :type seemingly cannot be a form to be evaluated on demand, so we 550 ;; :type seemingly cannot be a form to be evaluated on demand, so we
551 ;; endeavor to keep it up-to-date with `so-long-action-alist' by 551 ;; endeavour to keep it up-to-date with `so-long-action-alist' by
552 ;; calling this from `so-long--action-alist-setter'. 552 ;; calling this from `so-long--action-alist-setter'.
553 `(radio ,@(mapcar (lambda (x) (list 'const :tag (cadr x) (car x))) 553 `(radio ,@(mapcar (lambda (x) (list 'const :tag (cadr x) (car x)))
554 (assq-delete-all nil so-long-action-alist)) 554 (assq-delete-all nil so-long-action-alist))
@@ -609,7 +609,7 @@ will be automatically processed; but custom actions can also do these things.
609The value `longlines-mode' causes that minor mode to be enabled. See 609The value `longlines-mode' causes that minor mode to be enabled. See
610longlines.el for more details. 610longlines.el for more details.
611 611
612Each action likewise determines the behavior of `so-long-revert'. 612Each action likewise determines the behaviour of `so-long-revert'.
613 613
614If the value is nil, or not defined in `so-long-action-alist', then no action 614If the value is nil, or not defined in `so-long-action-alist', then no action
615will be taken." 615will be taken."
@@ -740,7 +740,7 @@ was established."
740 ) 740 )
741 ;; It's not clear to me whether all of these would be problematic, but they 741 ;; It's not clear to me whether all of these would be problematic, but they
742 ;; seemed like reasonable targets. Some are certainly excessive in smaller 742 ;; seemed like reasonable targets. Some are certainly excessive in smaller
743 ;; buffers of minified code, but we should be aiming to maximize performance 743 ;; buffers of minified code, but we should be aiming to maximise performance
744 ;; by default, so that Emacs is as responsive as we can manage in even very 744 ;; by default, so that Emacs is as responsive as we can manage in even very
745 ;; large buffers of minified code. 745 ;; large buffers of minified code.
746 "List of buffer-local minor modes to explicitly disable. 746 "List of buffer-local minor modes to explicitly disable.
@@ -756,7 +756,7 @@ By default this happens if `so-long-action' is set to either `so-long-mode'
756or `so-long-minor-mode'. If `so-long-revert' is subsequently invoked, then the 756or `so-long-minor-mode'. If `so-long-revert' is subsequently invoked, then the
757disabled modes are re-enabled by calling them with the numeric argument 1. 757disabled modes are re-enabled by calling them with the numeric argument 1.
758 758
759`so-long-hook' can be used where more custom behavior is desired. 759`so-long-hook' can be used where more custom behaviour is desired.
760 760
761Please submit bug reports to recommend additional modes for this list, whether 761Please submit bug reports to recommend additional modes for this list, whether
762they are in Emacs core, GNU ELPA, or elsewhere." 762they are in Emacs core, GNU ELPA, or elsewhere."
@@ -781,9 +781,20 @@ If `so-long-revert' is subsequently invoked, then the variables are restored
781to their original states. 781to their original states.
782 782
783The combination of `line-move-visual' (enabled) and `truncate-lines' (disabled) 783The combination of `line-move-visual' (enabled) and `truncate-lines' (disabled)
784is important for maximizing responsiveness when moving vertically within an 784is important for maximising responsiveness when moving vertically within an
785extremely long line, as otherwise the full length of the line may need to be 785extremely long line, as otherwise the full length of the line may need to be
786scanned to find the next position." 786scanned to find the next position.
787
788Bidirectional text display -- especially handling the large quantities of
789nested parentheses which are liable to occur in minified programming code --
790can be very expensive for extremely long lines, and so this support is disabled
791by default (insofar as is supported; in particular `bidi-inhibit-bpa' is not
792available in Emacs versions < 27). For more information refer to info node
793`(emacs) Bidirectional Editing' and info node `(elisp) Bidirectional Display'.
794
795Buffers are made read-only by default to prevent potentially-slow editing from
796occurring inadvertantly, as buffers with excessively long lines are likely not
797intended to be edited manually."
787 :type '(alist :key-type (variable :tag "Variable") 798 :type '(alist :key-type (variable :tag "Variable")
788 :value-type (sexp :tag "Value")) 799 :value-type (sexp :tag "Value"))
789 :options '((bidi-inhibit-bpa boolean) 800 :options '((bidi-inhibit-bpa boolean)
@@ -822,18 +833,18 @@ If nil, no mode line indicator will be displayed."
822 833
823(defface so-long-mode-line-active 834(defface so-long-mode-line-active
824 '((t :inherit mode-line-emphasis)) 835 '((t :inherit mode-line-emphasis))
825 "Face for `so-long-mode-line-info' when mitigation is active." 836 "Face for `so-long-mode-line-info' when mitigations are active."
826 :package-version '(so-long . "1.0")) 837 :package-version '(so-long . "1.0"))
827 838
828(defface so-long-mode-line-inactive 839(defface so-long-mode-line-inactive
829 '((t :inherit mode-line-inactive)) 840 '((t :inherit mode-line-inactive))
830 "Face for `so-long-mode-line-info' when mitigation has been reverted." 841 "Face for `so-long-mode-line-info' when mitigations have been reverted."
831 :package-version '(so-long . "1.0")) 842 :package-version '(so-long . "1.0"))
832 843
833;; Modes that go slowly and line lengths excessive 844;; Modes that go slowly and line lengths excessive
834;; Font-lock performance becoming oppressive 845;; Font-lock performance becoming oppressive
835;; All of my CPU tied up with strings 846;; All of my CPU tied up with strings
836;; These are a few of my least-favorite things 847;; These are a few of my least-favourite things
837 848
838(defvar-local so-long-original-values nil 849(defvar-local so-long-original-values nil
839 "Alist holding the buffer's original `major-mode' value, and other data. 850 "Alist holding the buffer's original `major-mode' value, and other data.
@@ -985,7 +996,7 @@ Displayed as part of `mode-line-misc-info'.
985 996
986`so-long-mode-line-label' defines the text to be displayed (if any). 997`so-long-mode-line-label' defines the text to be displayed (if any).
987 998
988Face `so-long-mode-line-active' is used while mitigation is active, and 999Face `so-long-mode-line-active' is used while mitigations are active, and
989`so-long-mode-line-inactive' is used if `so-long-revert' is called. 1000`so-long-mode-line-inactive' is used if `so-long-revert' is called.
990 1001
991Not displayed when `so-long-mode' is enabled, as the major mode construct 1002Not displayed when `so-long-mode' is enabled, as the major mode construct
@@ -1038,7 +1049,9 @@ This is the default value of `so-long-predicate'."
1038 (let ((count 0) start) 1049 (let ((count 0) start)
1039 (save-excursion 1050 (save-excursion
1040 (goto-char (point-min)) 1051 (goto-char (point-min))
1041 (when so-long-skip-leading-comments 1052 (when (and so-long-skip-leading-comments
1053 (or comment-use-syntax ;; Refer to `comment-forward'.
1054 (and comment-start-skip comment-end-skip)))
1042 ;; Skip the shebang line, if any. This is not necessarily comment 1055 ;; Skip the shebang line, if any. This is not necessarily comment
1043 ;; syntax, so we need to treat it specially. 1056 ;; syntax, so we need to treat it specially.
1044 (when (looking-at "#!") 1057 (when (looking-at "#!")
@@ -1131,7 +1144,7 @@ This minor mode is a standard `so-long-action' option."
1131 (if so-long-minor-mode ;; We are enabling the mode. 1144 (if so-long-minor-mode ;; We are enabling the mode.
1132 (progn 1145 (progn
1133 ;; Housekeeping. `so-long-minor-mode' might be invoked directly rather 1146 ;; Housekeeping. `so-long-minor-mode' might be invoked directly rather
1134 ;; than via `so-long', so replicate the necessary behaviors. The minor 1147 ;; than via `so-long', so replicate the necessary behaviours. The minor
1135 ;; mode also cares about whether `so-long' was already active, as we do 1148 ;; mode also cares about whether `so-long' was already active, as we do
1136 ;; not want to remember values which were potentially overridden already. 1149 ;; not want to remember values which were potentially overridden already.
1137 (unless (or so-long--calling so-long--active) 1150 (unless (or so-long--calling so-long--active)
@@ -1203,9 +1216,9 @@ values), despite potential performance issues, type \\[so-long-revert].
1203 1216
1204Use \\[so-long-commentary] for more information. 1217Use \\[so-long-commentary] for more information.
1205 1218
1206Use \\[so-long-customize] to configure the behavior." 1219Use \\[so-long-customize] to configure the behaviour."
1207 ;; Housekeeping. `so-long-mode' might be invoked directly rather than via 1220 ;; Housekeeping. `so-long-mode' might be invoked directly rather than via
1208 ;; `so-long', so replicate the necessary behaviors. We could use this same 1221 ;; `so-long', so replicate the necessary behaviours. We could use this same
1209 ;; test in `so-long-after-change-major-mode' to run `so-long-hook', but that's 1222 ;; test in `so-long-after-change-major-mode' to run `so-long-hook', but that's
1210 ;; not so obviously the right thing to do, so I've omitted it for now. 1223 ;; not so obviously the right thing to do, so I've omitted it for now.
1211 (unless so-long--calling 1224 (unless so-long--calling
@@ -1251,7 +1264,7 @@ Use \\[so-long-customize] to configure the behavior."
1251This advice acts before `so-long-mode', with the previous mode still active." 1264This advice acts before `so-long-mode', with the previous mode still active."
1252 (unless (derived-mode-p 'so-long-mode) 1265 (unless (derived-mode-p 'so-long-mode)
1253 ;; Housekeeping. `so-long-mode' might be invoked directly rather than 1266 ;; Housekeeping. `so-long-mode' might be invoked directly rather than
1254 ;; via `so-long', so replicate the necessary behaviors. 1267 ;; via `so-long', so replicate the necessary behaviours.
1255 (unless so-long--calling 1268 (unless so-long--calling
1256 (so-long-remember-all :reset)) 1269 (so-long-remember-all :reset))
1257 ;; Remember the original major mode, regardless. 1270 ;; Remember the original major mode, regardless.
@@ -1336,7 +1349,7 @@ This is the `so-long-revert-function' for `so-long-mode'."
1336 ;; Emacs 26+ has already called `hack-local-variables' (during 1349 ;; Emacs 26+ has already called `hack-local-variables' (during
1337 ;; `run-mode-hooks'; provided there was a `buffer-file-name'), but for older 1350 ;; `run-mode-hooks'; provided there was a `buffer-file-name'), but for older
1338 ;; versions we need to call it here. In Emacs 26+ the revised 'HANDLE-MODE' 1351 ;; versions we need to call it here. In Emacs 26+ the revised 'HANDLE-MODE'
1339 ;; argument is set to `no-mode' (being the non-nil-and-non-t behavior), 1352 ;; argument is set to `no-mode' (being the non-nil-and-non-t behaviour),
1340 ;; which we mimic here by binding `so-long--hack-local-variables-no-mode', 1353 ;; which we mimic here by binding `so-long--hack-local-variables-no-mode',
1341 ;; in order to prevent a local 'mode' variable from clobbering the major 1354 ;; in order to prevent a local 'mode' variable from clobbering the major
1342 ;; mode we have just called. 1355 ;; mode we have just called.
@@ -1373,7 +1386,7 @@ because we do not want to downgrade the major mode in that scenario."
1373 ;; Act only if `so-long-mode' would be enabled by the current action. 1386 ;; Act only if `so-long-mode' would be enabled by the current action.
1374 (when (and (symbolp (so-long-function)) 1387 (when (and (symbolp (so-long-function))
1375 (provided-mode-derived-p (so-long-function) 'so-long-mode)) 1388 (provided-mode-derived-p (so-long-function) 'so-long-mode))
1376 ;; Downgrade from `so-long-mode' to the `so-long-minor-mode' behavior. 1389 ;; Downgrade from `so-long-mode' to the `so-long-minor-mode' behaviour.
1377 (setq so-long-function 'turn-on-so-long-minor-mode 1390 (setq so-long-function 'turn-on-so-long-minor-mode
1378 so-long-revert-function 'turn-off-so-long-minor-mode)))) 1391 so-long-revert-function 'turn-off-so-long-minor-mode))))
1379 1392
@@ -1393,7 +1406,7 @@ and cannot be conveniently intercepted, so we are forced to replicate it here.
1393 1406
1394This special-case code will ultimately be removed from Emacs, as it exists to 1407This special-case code will ultimately be removed from Emacs, as it exists to
1395deal with a deprecated feature; but until then we need to replicate it in order 1408deal with a deprecated feature; but until then we need to replicate it in order
1396to inhibit our own behavior in the presence of a header comment `mode' 1409to inhibit our own behaviour in the presence of a header comment `mode'
1397declaration. 1410declaration.
1398 1411
1399If a file-local mode is detected in the header comment, then we call the 1412If a file-local mode is detected in the header comment, then we call the
@@ -1528,7 +1541,7 @@ by testing the value against `major-mode'; but as we may have changed the
1528major mode to `so-long-mode' by this point, that protection is insufficient 1541major mode to `so-long-mode' by this point, that protection is insufficient
1529and so we need to perform our own test. 1542and so we need to perform our own test.
1530 1543
1531We likewise need to support an equivalent of the `no-mode' behavior in 26.1+ 1544We likewise need to support an equivalent of the `no-mode' behaviour in 26.1+
1532to ensure that `so-long-mode-revert' will not restore a file-local mode again 1545to ensure that `so-long-mode-revert' will not restore a file-local mode again
1533after it has already reverted to the original mode. 1546after it has already reverted to the original mode.
1534 1547
@@ -1661,7 +1674,7 @@ Equivalent to calling (global-so-long-mode 0)"
1661 1674
1662;;;###autoload 1675;;;###autoload
1663(define-minor-mode global-so-long-mode 1676(define-minor-mode global-so-long-mode
1664 "Toggle automated performance mitigation for files with long lines. 1677 "Toggle automated performance mitigations for files with long lines.
1665 1678
1666Many Emacs modes struggle with buffers which contain excessively long lines, 1679Many Emacs modes struggle with buffers which contain excessively long lines,
1667and may consequently cause unacceptable performance issues. 1680and may consequently cause unacceptable performance issues.
@@ -1675,7 +1688,7 @@ When such files are detected by `so-long-predicate', we invoke the selected
1675 1688
1676Use \\[so-long-commentary] for more information. 1689Use \\[so-long-commentary] for more information.
1677 1690
1678Use \\[so-long-customize] to configure the behavior." 1691Use \\[so-long-customize] to configure the behaviour."
1679 :global t 1692 :global t
1680 :group 'so-long 1693 :group 'so-long
1681 (if global-so-long-mode 1694 (if global-so-long-mode
@@ -1810,9 +1823,10 @@ If it appears in `%s', you should remove it."
1810 ;; Update to version 1.0 from earlier versions: 1823 ;; Update to version 1.0 from earlier versions:
1811 (when (version< so-long-version "1.0") 1824 (when (version< so-long-version "1.0")
1812 (remove-hook 'change-major-mode-hook 'so-long-change-major-mode) 1825 (remove-hook 'change-major-mode-hook 'so-long-change-major-mode)
1813 (require 'advice) 1826 (eval-and-compile (require 'advice)) ;; Both macros and functions.
1814 (declare-function ad-find-advice "advice") 1827 (declare-function ad-find-advice "advice")
1815 (declare-function ad-remove-advice "advice") 1828 (declare-function ad-remove-advice "advice")
1829 (declare-function ad-activate "advice")
1816 (when (ad-find-advice 'hack-local-variables 'after 'so-long--file-local-mode) 1830 (when (ad-find-advice 'hack-local-variables 'after 'so-long--file-local-mode)
1817 (ad-remove-advice 'hack-local-variables 'after 'so-long--file-local-mode) 1831 (ad-remove-advice 'hack-local-variables 'after 'so-long--file-local-mode)
1818 (ad-activate 'hack-local-variables)) 1832 (ad-activate 'hack-local-variables))
@@ -1864,8 +1878,8 @@ If it appears in `%s', you should remove it."
1864; LocalWords: noerror selectable mapc sgml nxml hl flydiff defs arg Phil Sainty 1878; LocalWords: noerror selectable mapc sgml nxml hl flydiff defs arg Phil Sainty
1865; LocalWords: defadvice nadvice whitespace ie bos eos eobp origmode un Un setq 1879; LocalWords: defadvice nadvice whitespace ie bos eos eobp origmode un Un setq
1866; LocalWords: docstring auf Wiedersehen longlines alist autoload Refactored Inc 1880; LocalWords: docstring auf Wiedersehen longlines alist autoload Refactored Inc
1867; LocalWords: MERCHANTABILITY RET REGEXP VAR ELPA WS EmacsWiki eval 1881; LocalWords: MERCHANTABILITY RET REGEXP VAR ELPA WS mitigations EmacsWiki eval
1868; LocalWords: rx filename filenames 1882; LocalWords: rx filename filenames bidi bpa
1869 1883
1870;; So long, farewell, auf Wiedersehen, goodbye 1884;; So long, farewell, auf Wiedersehen, goodbye
1871;; You have to go, this code is minified 1885;; You have to go, this code is minified
diff --git a/lisp/subr.el b/lisp/subr.el
index 2ef28b1ce6a..0ae636b68b4 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -888,6 +888,10 @@ side-effects, and the argument LIST is not modified."
888 888
889;;;; Keymap support. 889;;;; Keymap support.
890 890
891;; Declare before first use of `save-match-data',
892;; where it is used internally.
893(defvar save-match-data-internal)
894
891(defun kbd (keys) 895(defun kbd (keys)
892 "Convert KEYS to the internal Emacs key representation. 896 "Convert KEYS to the internal Emacs key representation.
893KEYS should be a string in the format returned by commands such 897KEYS should be a string in the format returned by commands such
@@ -4110,8 +4114,6 @@ MODES is as for `set-default-file-modes'."
4110 4114
4111;;; Matching and match data. 4115;;; Matching and match data.
4112 4116
4113(defvar save-match-data-internal)
4114
4115;; We use save-match-data-internal as the local variable because 4117;; We use save-match-data-internal as the local variable because
4116;; that works ok in practice (people should not use that variable elsewhere). 4118;; that works ok in practice (people should not use that variable elsewhere).
4117;; We used to use an uninterned symbol; the compiler handles that properly 4119;; We used to use an uninterned symbol; the compiler handles that properly
diff --git a/lisp/tar-mode.el b/lisp/tar-mode.el
index 73978ffc4a7..5cf09f9055e 100644
--- a/lisp/tar-mode.el
+++ b/lisp/tar-mode.el
@@ -922,6 +922,56 @@ actually appear on disk when you save the tar-file's buffer."
922 (setq buffer-undo-list nil)))) 922 (setq buffer-undo-list nil))))
923 buffer)) 923 buffer))
924 924
925(defun tar-goto-file (file)
926 "Go to FILE in the current buffer.
927FILE should be a relative file name. If FILE can't be found,
928return nil. Otherwise point is returned."
929 (let ((start (point))
930 found)
931 (goto-char (point-min))
932 (while (and (not found)
933 (not (eobp)))
934 (forward-line 1)
935 (when-let ((descriptor (ignore-errors (tar-get-descriptor))))
936 (when (equal (tar-header-name descriptor) file)
937 (setq found t))))
938 (if (not found)
939 (progn
940 (goto-char start)
941 nil)
942 (point))))
943
944(defun tar-next-file-displayer (file regexp n)
945 "Return a closure to display the next file after FILE that matches REGEXP."
946 (let ((short (replace-regexp-in-string "\\`.*!" "" file))
947 next)
948 ;; The tar buffer chops off leading "./", so do the same
949 ;; here.
950 (setq short (replace-regexp-in-string "\\`\\./" "" file))
951 (tar-goto-file short)
952 (while (and (not next)
953 ;; Stop if we reach the end/start of the buffer.
954 (if (> n 0)
955 (not (eobp))
956 (not (save-excursion
957 (beginning-of-line)
958 (bobp)))))
959 (tar-next-line n)
960 (when-let ((descriptor (ignore-errors (tar-get-descriptor))))
961 (let ((candidate (tar-header-name descriptor))
962 (buffer (current-buffer)))
963 (when (and candidate
964 (string-match-p regexp candidate))
965 (setq next (lambda ()
966 (kill-buffer (current-buffer))
967 (switch-to-buffer buffer)
968 (tar-extract)))))))
969 (unless next
970 ;; If we didn't find a next/prev file, then restore
971 ;; point.
972 (tar-goto-file short))
973 next))
974
925(defun tar-extract (&optional other-window-p) 975(defun tar-extract (&optional other-window-p)
926 "In Tar mode, extract this entry of the tar file into its own buffer." 976 "In Tar mode, extract this entry of the tar file into its own buffer."
927 (interactive) 977 (interactive)
diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index 2cd99787e8a..cc5879880c8 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -67,7 +67,7 @@
67 67
68(defconst scss-at-ids 68(defconst scss-at-ids
69 '("at-root" "content" "debug" "each" "else" "else if" "error" "extend" 69 '("at-root" "content" "debug" "each" "else" "else if" "error" "extend"
70 "for" "function" "if" "import" "include" "mixin" "return" "warn" 70 "for" "function" "if" "import" "include" "mixin" "return" "use" "warn"
71 "while") 71 "while")
72 "Additional identifiers that appear in the form @foo in SCSS.") 72 "Additional identifiers that appear in the form @foo in SCSS.")
73 73
diff --git a/lisp/textmodes/sgml-mode.el b/lisp/textmodes/sgml-mode.el
index b5ff6a69671..1672dce4f23 100644
--- a/lisp/textmodes/sgml-mode.el
+++ b/lisp/textmodes/sgml-mode.el
@@ -1806,6 +1806,7 @@ This takes effect when first loading the library.")
1806 (define-key map "\C-c\C-cc" 'html-checkboxes) 1806 (define-key map "\C-c\C-cc" 'html-checkboxes)
1807 (define-key map "\C-c\C-cl" 'html-list-item) 1807 (define-key map "\C-c\C-cl" 'html-list-item)
1808 (define-key map "\C-c\C-ch" 'html-href-anchor) 1808 (define-key map "\C-c\C-ch" 'html-href-anchor)
1809 (define-key map "\C-c\C-cf" 'html-href-anchor-file)
1809 (define-key map "\C-c\C-cn" 'html-name-anchor) 1810 (define-key map "\C-c\C-cn" 'html-name-anchor)
1810 (define-key map "\C-c\C-c#" 'html-id-anchor) 1811 (define-key map "\C-c\C-c#" 'html-id-anchor)
1811 (define-key map "\C-c\C-ci" 'html-image) 1812 (define-key map "\C-c\C-ci" 'html-image)
@@ -1818,6 +1819,7 @@ This takes effect when first loading the library.")
1818 (define-key map "\C-cc" 'html-checkboxes) 1819 (define-key map "\C-cc" 'html-checkboxes)
1819 (define-key map "\C-cl" 'html-list-item) 1820 (define-key map "\C-cl" 'html-list-item)
1820 (define-key map "\C-ch" 'html-href-anchor) 1821 (define-key map "\C-ch" 'html-href-anchor)
1822 (define-key map "\C-cf" 'html-href-anchor-file)
1821 (define-key map "\C-cn" 'html-name-anchor) 1823 (define-key map "\C-cn" 'html-name-anchor)
1822 (define-key map "\C-c#" 'html-id-anchor) 1824 (define-key map "\C-c#" 'html-id-anchor)
1823 (define-key map "\C-ci" 'html-image) 1825 (define-key map "\C-ci" 'html-image)
@@ -1845,7 +1847,8 @@ This takes effect when first loading the library.")
1845 (define-key menu-map "\n" '("Line Break" . html-line)) 1847 (define-key menu-map "\n" '("Line Break" . html-line))
1846 (define-key menu-map "\r" '("Paragraph" . html-paragraph)) 1848 (define-key menu-map "\r" '("Paragraph" . html-paragraph))
1847 (define-key menu-map "i" '("Image" . html-image)) 1849 (define-key menu-map "i" '("Image" . html-image))
1848 (define-key menu-map "h" '("Href Anchor" . html-href-anchor)) 1850 (define-key menu-map "h" '("Href Anchor URL" . html-href-anchor))
1851 (define-key menu-map "f" '("Href Anchor File" . html-href-anchor-file))
1849 (define-key menu-map "n" '("Name Anchor" . html-name-anchor)) 1852 (define-key menu-map "n" '("Name Anchor" . html-name-anchor))
1850 (define-key menu-map "#" '("ID Anchor" . html-id-anchor)) 1853 (define-key menu-map "#" '("ID Anchor" . html-id-anchor))
1851 map) 1854 map)
@@ -2453,6 +2456,11 @@ HTML Autoview mode is a buffer-local minor mode for use with
2453 ;; '(setq input "http:") 2456 ;; '(setq input "http:")
2454 "<a href=\"" str "\">" _ "</a>") 2457 "<a href=\"" str "\">" _ "</a>")
2455 2458
2459(define-skeleton html-href-anchor-file
2460 "HTML anchor tag with href attribute (from a local file)."
2461 (file-relative-name (read-file-name "File name: ") default-directory)
2462 "<a href=\"" str "\">" _ "</a>")
2463
2456(define-skeleton html-name-anchor 2464(define-skeleton html-name-anchor
2457 "HTML anchor tag with name attribute." 2465 "HTML anchor tag with name attribute."
2458 "Name: " 2466 "Name: "
diff --git a/lisp/thingatpt.el b/lisp/thingatpt.el
index 1a15df33e50..483a2c9bd83 100644
--- a/lisp/thingatpt.el
+++ b/lisp/thingatpt.el
@@ -334,7 +334,7 @@ the bounds of a possible ill-formed URI (one lacking a scheme)."
334 ;; may contain parentheses but may not contain spaces (RFC3986). 334 ;; may contain parentheses but may not contain spaces (RFC3986).
335 (let* ((allowed-chars "--:=&?$+@-Z_[:alpha:]~#,%;*()!'") 335 (let* ((allowed-chars "--:=&?$+@-Z_[:alpha:]~#,%;*()!'")
336 (skip-before "^[0-9a-zA-Z]") 336 (skip-before "^[0-9a-zA-Z]")
337 (skip-after ":;.,!?") 337 (skip-after ":;.,!?'")
338 (pt (point)) 338 (pt (point))
339 (beg (save-excursion 339 (beg (save-excursion
340 (skip-chars-backward allowed-chars) 340 (skip-chars-backward allowed-chars)
diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el
index 95ced7b8d09..cb0657e70a0 100644
--- a/lisp/vc/vc-hg.el
+++ b/lisp/vc/vc-hg.el
@@ -186,6 +186,16 @@ highlighting the Log View buffer."
186 :group 'vc-hg 186 :group 'vc-hg
187 :version "24.5") 187 :version "24.5")
188 188
189(defcustom vc-hg-create-bookmark t
190 "This controls whether `vc-create-tag' will create a bookmark or branch.
191If nil, named branch will be created.
192If t, bookmark will be created.
193If `ask', you will be prompted for a branch type."
194 :type '(choice (const :tag "No" nil)
195 (const :tag "Yes" t)
196 (const :tag "Ask" ask))
197 :version "28.1")
198
189 199
190;; Clear up the cache to force vc-call to check again and discover 200;; Clear up the cache to force vc-call to check again and discover
191;; new functions when we reload this file. 201;; new functions when we reload this file.
@@ -625,10 +635,18 @@ Optional arg REVISION is a revision to annotate from."
625;;; Tag system 635;;; Tag system
626 636
627(defun vc-hg-create-tag (dir name branchp) 637(defun vc-hg-create-tag (dir name branchp)
628 "Attach the tag NAME to the state of the working copy." 638 "Create tag NAME in repo in DIR. Create branch if BRANCHP.
639Variable `vc-hg-create-bookmark' controls what kind of branch will be created."
629 (let ((default-directory dir)) 640 (let ((default-directory dir))
630 (and (vc-hg-command nil 0 nil "status") 641 (vc-hg-command nil 0 nil
631 (vc-hg-command nil 0 nil (if branchp "bookmark" "tag") name)))) 642 (if branchp
643 (if (if (eq vc-hg-create-bookmark 'ask)
644 (yes-or-no-p "Create bookmark instead of branch? ")
645 vc-hg-create-bookmark)
646 "bookmark"
647 "branch")
648 "tag")
649 name)))
632 650
633(defun vc-hg-retrieve-tag (dir name _update) 651(defun vc-hg-retrieve-tag (dir name _update)
634 "Retrieve the version tagged by NAME of all registered files at or below DIR." 652 "Retrieve the version tagged by NAME of all registered files at or below DIR."
@@ -1366,25 +1384,28 @@ REV is the revision to check out into WORKFILE."
1366 (vc-run-delayed 1384 (vc-run-delayed
1367 (vc-hg-after-dir-status update-function))) 1385 (vc-hg-after-dir-status update-function)))
1368 1386
1369(defun vc-hg-dir-extra-header (name &rest commands)
1370 (concat (propertize name 'face 'font-lock-type-face)
1371 (propertize
1372 (with-temp-buffer
1373 (apply 'vc-hg-command (current-buffer) 0 nil commands)
1374 (buffer-substring-no-properties (point-min) (1- (point-max))))
1375 'face 'font-lock-variable-name-face)))
1376
1377(defun vc-hg-dir-extra-headers (dir) 1387(defun vc-hg-dir-extra-headers (dir)
1378 "Generate extra status headers for a Mercurial tree." 1388 "Generate extra status headers for a repository in DIR.
1389This runs the command \"hg summary\"."
1379 (let ((default-directory dir)) 1390 (let ((default-directory dir))
1380 (concat 1391 (with-temp-buffer
1381 (vc-hg-dir-extra-header "Root : " "root") "\n" 1392 (vc-hg-command t 0 nil "summary")
1382 (vc-hg-dir-extra-header "Branch : " "id" "-b") "\n" 1393 (goto-char (point-min))
1383 (vc-hg-dir-extra-header "Tags : " "id" "-t") ; "\n" 1394 (mapconcat
1384 ;; these change after each commit 1395 #'identity
1385 ;; (vc-hg-dir-extra-header "Local num : " "id" "-n") "\n" 1396 (let (result)
1386 ;; (vc-hg-dir-extra-header "Global id : " "id" "-i") 1397 (while (not (eobp))
1387 ))) 1398 (push
1399 (let ((entry (if (looking-at "\\([^ ].*\\): \\(.*\\)")
1400 (cons (capitalize (match-string 1)) (match-string 2))
1401 (cons "" (buffer-substring (point) (line-end-position))))))
1402 (concat
1403 (propertize (format "%-11s: " (car entry)) 'face 'font-lock-type-face)
1404 (propertize (cdr entry) 'face 'font-lock-variable-name-face)))
1405 result)
1406 (forward-line))
1407 (nreverse result))
1408 "\n"))))
1388 1409
1389(defun vc-hg-log-incoming (buffer remote-location) 1410(defun vc-hg-log-incoming (buffer remote-location)
1390 (vc-setup-buffer buffer) 1411 (vc-setup-buffer buffer)
diff --git a/lisp/wdired.el b/lisp/wdired.el
index 768b8f597b4..b98becfafe7 100644
--- a/lisp/wdired.el
+++ b/lisp/wdired.el
@@ -609,7 +609,10 @@ Optional arguments are ignored."
609(defun wdired--restore-dired-filename-prop (beg end _len) 609(defun wdired--restore-dired-filename-prop (beg end _len)
610 (save-match-data 610 (save-match-data
611 (save-excursion 611 (save-excursion
612 (let ((lep (line-end-position))) 612 (let ((lep (line-end-position))
613 (used-F (dired-check-switches
614 dired-actual-switches
615 "F" "classify")))
613 (beginning-of-line) 616 (beginning-of-line)
614 (when (re-search-forward 617 (when (re-search-forward
615 directory-listing-before-filename-regexp lep t) 618 directory-listing-before-filename-regexp lep t)
@@ -623,13 +626,17 @@ Optional arguments are ignored."
623 (and (re-search-backward 626 (and (re-search-backward
624 dired-permission-flags-regexp nil t) 627 dired-permission-flags-regexp nil t)
625 (looking-at "l") 628 (looking-at "l")
626 (search-forward " -> " lep t)) 629 ;; macOS and Ultrix adds "@" to the end
630 ;; of symlinks when using -F.
631 (if (and used-F
632 dired-ls-F-marks-symlinks)
633 (re-search-forward "@? -> " lep t)
634 (search-forward " -> " lep t)))
627 ;; When dired-listing-switches includes "F" 635 ;; When dired-listing-switches includes "F"
628 ;; or "classify", don't treat appended 636 ;; or "classify", don't treat appended
629 ;; indicator characters as part of the file 637 ;; indicator characters as part of the file
630 ;; name (bug#34915). 638 ;; name (bug#34915).
631 (and (dired-check-switches dired-actual-switches 639 (and used-F
632 "F" "classify")
633 (re-search-forward "[*/@|=>]$" lep t))) 640 (re-search-forward "[*/@|=>]$" lep t)))
634 (goto-char (match-beginning 0)) 641 (goto-char (match-beginning 0))
635 lep)) 642 lep))
diff --git a/lisp/whitespace.el b/lisp/whitespace.el
index 47434bf3d2e..42c4b61daff 100644
--- a/lisp/whitespace.el
+++ b/lisp/whitespace.el
@@ -283,7 +283,8 @@
283 '(face 283 '(face
284 tabs spaces trailing lines space-before-tab newline 284 tabs spaces trailing lines space-before-tab newline
285 indentation empty space-after-tab 285 indentation empty space-after-tab
286 space-mark tab-mark newline-mark) 286 space-mark tab-mark newline-mark
287 missing-newline-at-eof)
287 "Specify which kind of blank is visualized. 288 "Specify which kind of blank is visualized.
288 289
289It's a list containing some or all of the following values: 290It's a list containing some or all of the following values:
@@ -326,6 +327,11 @@ It's a list containing some or all of the following values:
326 It has effect only if `face' (see above) 327 It has effect only if `face' (see above)
327 is present in `whitespace-style'. 328 is present in `whitespace-style'.
328 329
330 missing-newline-at-eof Missing newline at the end of the file is
331 visualized via faces.
332 It has effect only if `face' (see above)
333 is present in `whitespace-style'.
334
329 empty empty lines at beginning and/or end of buffer 335 empty empty lines at beginning and/or end of buffer
330 are visualized via faces. 336 are visualized via faces.
331 It has effect only if `face' (see above) 337 It has effect only if `face' (see above)
@@ -586,6 +592,10 @@ line. Used when `whitespace-style' includes the value `indentation'.")
586 "Face used to visualize big indentation." 592 "Face used to visualize big indentation."
587 :group 'whitespace) 593 :group 'whitespace)
588 594
595(defface whitespace-missing-newline-at-eof
596 '((((class mono)) :inverse-video t :weight bold :underline t)
597 (t :background "#d0d040" :foreground "black"))
598 "Face used to visualize missing newline at the end of the file.")
589 599
590(defvar whitespace-empty 'whitespace-empty 600(defvar whitespace-empty 'whitespace-empty
591 "Symbol face used to visualize empty lines at beginning and/or end of buffer. 601 "Symbol face used to visualize empty lines at beginning and/or end of buffer.
@@ -1700,6 +1710,8 @@ cleaning up these problems."
1700 (whitespace-space-after-tab-regexp 'tab)) 1710 (whitespace-space-after-tab-regexp 'tab))
1701 ((eq (car option) 'space-after-tab::space) 1711 ((eq (car option) 'space-after-tab::space)
1702 (whitespace-space-after-tab-regexp 'space)) 1712 (whitespace-space-after-tab-regexp 'space))
1713 ((eq (car option) 'missing-newline-at-eof)
1714 "[^\n]\\'")
1703 (t 1715 (t
1704 (cdr option))))) 1716 (cdr option)))))
1705 (when (re-search-forward regexp rend t) 1717 (when (re-search-forward regexp rend t)
@@ -2122,7 +2134,16 @@ resultant list will be returned."
2122 ((memq 'space-after-tab::space whitespace-active-style) 2134 ((memq 'space-after-tab::space whitespace-active-style)
2123 ;; Show SPACEs after TAB (TABs). 2135 ;; Show SPACEs after TAB (TABs).
2124 (whitespace-space-after-tab-regexp 'space))) 2136 (whitespace-space-after-tab-regexp 'space)))
2125 1 whitespace-space-after-tab t))))) 2137 1 whitespace-space-after-tab t)))
2138 ,@(when (memq 'missing-newline-at-eof whitespace-active-style)
2139 ;; Show missing newline.
2140 `(("[^\n]\\'" 0
2141 ;; Don't mark the end of the buffer is point is there --
2142 ;; it probably means that the user is typing something
2143 ;; at the end of the buffer.
2144 (and (/= whitespace-point (point-max))
2145 'whitespace-missing-newline-at-eof)
2146 t)))))
2126 (font-lock-add-keywords nil whitespace-font-lock-keywords t) 2147 (font-lock-add-keywords nil whitespace-font-lock-keywords t)
2127 (font-lock-flush))) 2148 (font-lock-flush)))
2128 2149
diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el
index 284fd1d6cbd..ea7e266e0d0 100644
--- a/lisp/wid-edit.el
+++ b/lisp/wid-edit.el
@@ -577,6 +577,63 @@ respectively."
577 (if (and widget (funcall function widget maparg)) 577 (if (and widget (funcall function widget maparg))
578 (setq overlays nil))))) 578 (setq overlays nil)))))
579 579
580(defun widget-describe (&optional widget-or-pos)
581 "Describe the widget at point.
582Displays a buffer with information about the widget (e.g., its actions) as well
583as a link to browse all the properties of the widget.
584
585This command resolves the indirection of widgets running the action of its
586parents, so the real action executed can be known.
587
588When called from Lisp, pass WIDGET-OR-POS as the widget to describe,
589or a buffer position where a widget is present. If WIDGET-OR-POS is nil,
590the widget at point is the widget to describe."
591 (interactive "d")
592 (require 'wid-browse) ; The widget-browse widget.
593 (let ((widget (if (widgetp widget-or-pos)
594 widget-or-pos
595 (widget-at widget-or-pos)))
596 props)
597 (when widget
598 (help-setup-xref (list #'widget-describe widget)
599 (called-interactively-p 'interactive))
600 (setq props (list (cons 'action (widget--resolve-parent-action widget))
601 (cons 'mouse-down-action
602 (widget-get widget :mouse-down-action))))
603 (with-help-window (help-buffer)
604 (with-current-buffer (help-buffer)
605 (widget-insert "This widget's type is ")
606 (widget-create 'widget-browse :format "%[%v%]\n%d"
607 :doc (get (car widget) 'widget-documentation)
608 :help-echo "Browse this widget's properties"
609 widget)
610 (dolist (action '(action mouse-down-action))
611 (let ((name (symbol-name action))
612 (val (alist-get action props)))
613 (when (functionp val)
614 (widget-insert "\n\n" (propertize (capitalize name) 'face 'bold)
615 "'\nThe " name " of this widget is")
616 (if (symbolp val)
617 (progn (widget-insert " ")
618 (widget-create 'function-link :value val
619 :button-prefix "" :button-suffix ""
620 :help-echo "Describe this function"))
621 (widget-insert "\n")
622 (princ val)))))))
623 (widget-setup)
624 t)))
625
626(defun widget--resolve-parent-action (widget)
627 "Resolve the real action of WIDGET up its inheritance chain.
628Follow the WIDGET's parents, until its :action is no longer
629`widget-parent-action', and return its value."
630 (let ((action (widget-get widget :action))
631 (parent (widget-get widget :parent)))
632 (while (eq action 'widget-parent-action)
633 (setq parent (widget-get parent :parent)
634 action (widget-get parent :action)))
635 action))
636
580;;; Images. 637;;; Images.
581 638
582(defcustom widget-image-directory (file-name-as-directory 639(defcustom widget-image-directory (file-name-as-directory
diff --git a/lisp/x-dnd.el b/lisp/x-dnd.el
index b22af5cc770..1d49f462531 100644
--- a/lisp/x-dnd.el
+++ b/lisp/x-dnd.el
@@ -1,4 +1,4 @@
1;;; x-dnd.el --- drag and drop support for X 1;;; x-dnd.el --- drag and drop support for X -*- lexical-binding: t; -*-
2 2
3;; Copyright (C) 2004-2020 Free Software Foundation, Inc. 3;; Copyright (C) 2004-2020 Free Software Foundation, Inc.
4 4
@@ -32,7 +32,7 @@
32(require 'dnd) 32(require 'dnd)
33 33
34;;; Customizable variables 34;;; Customizable variables
35(defcustom x-dnd-test-function 'x-dnd-default-test-function 35(defcustom x-dnd-test-function #'x-dnd-default-test-function
36 "The function drag and drop uses to determine if to accept or reject a drop. 36 "The function drag and drop uses to determine if to accept or reject a drop.
37The function takes three arguments, WINDOW, ACTION and TYPES. 37The function takes three arguments, WINDOW, ACTION and TYPES.
38WINDOW is where the mouse is when the function is called. WINDOW may be a 38WINDOW is where the mouse is when the function is called. WINDOW may be a