commit e1a45c26c4b951d1d2407c2f3075164866d8a0ea (HEAD, refs/remotes/origin/master) Author: Eric Abrahamsen Date: Sun Apr 21 11:04:20 2019 -0700 New option for making Gnus window layouts atomic * lisp/gnus/gnus-win.el (gnus-use-atomic-windows): New boolean customization option. (gnus-configure-windows): When removing old window layouts, check for and remove atomicity. (gnus-configure-windows): When gnus-use-atomic-windows is non-nil, make Gnus window layouts atomic. * doc/misc/gnus.texi (Window Layout): Document. diff --git a/doc/misc/gnus.texi b/doc/misc/gnus.texi index fb9581f985..d535c1a49e 100644 --- a/doc/misc/gnus.texi +++ b/doc/misc/gnus.texi @@ -22923,6 +22923,14 @@ window is displayed vertically next to another window, you may also want to fiddle with @code{gnus-tree-minimize-window} to avoid having the windows resized. +Lastly, it's possible to make Gnus window layouts ``atomic'' +(@xref{Atomic Windows, , Atomic Windows, elisp, The GNU Emacs Lisp +Reference Manual}) by setting @code{gnus-use-atomic-windows} to +@code{t}. This will ensure that pop-up buffers (e.g. help or +completion buffers), will appear below or to the side of the entire +Gnus window layout and not, for example, squashed between the summary +and article buffers. + @subsection Window Configuration Names Here's a list of most of the currently known window configurations, diff --git a/etc/NEWS b/etc/NEWS index 20d2e62bd3..166c13363f 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1031,6 +1031,11 @@ Of course it will still find it if you have it in '~/.ecompleterc'. ** Gnus ++++ +*** New option 'gnus-use-atomic-windows' makes Gnus window layouts +atomic. See the "Atomic Windows" section of the Elisp manual for +details. + +++ *** There's a new value for 'gnus-article-date-headers', 'combined-local-lapsed', which will show both the time (in the local diff --git a/lisp/gnus/gnus-win.el b/lisp/gnus/gnus-win.el index e6906e99bb..cc3141cf63 100644 --- a/lisp/gnus/gnus-win.el +++ b/lisp/gnus/gnus-win.el @@ -39,6 +39,11 @@ :group 'gnus-windows :type 'boolean) +(defcustom gnus-use-atomic-windows t + "If non-nil, Gnus' window compositions will be atomic." + :type 'boolean + :version "27.1") + (defcustom gnus-window-min-width 2 "Minimum width of Gnus buffers." :group 'gnus-windows @@ -402,6 +407,15 @@ See the Gnus manual for an explanation of the syntax used.") (unless (gnus-buffer-live-p nntp-server-buffer) (nnheader-init-server-buffer)) + ;; Remove all 'window-atom parameters, as we're going to blast + ;; and recreate the window layout. + (when (window-parameter nil 'window-atom) + (let ((root (window-atom-root))) + (walk-window-subtree + (lambda (win) + (set-window-parameter win 'window-atom nil)) + root t))) + ;; Either remove all windows or just remove all Gnus windows. (let ((frame (selected-frame))) (unwind-protect @@ -423,6 +437,13 @@ See the Gnus manual for an explanation of the syntax used.") (set-buffer nntp-server-buffer) (gnus-configure-frame split) (run-hooks 'gnus-configure-windows-hook) + + ;; If we're using atomic windows, and the current frame has + ;; multiple windows, make them atomic. + (when (and gnus-use-atomic-windows + (window-parent (selected-window))) + (window-make-atom (window-parent (selected-window)))) + (when gnus-window-frame-focus (select-frame-set-input-focus (window-frame gnus-window-frame-focus))))))))) commit 14e7c01feeafd3852522c221187e7359d21079f5 Author: Stefan Kangas Date: Sat Sep 21 17:04:58 2019 +0200 * src/fns.c (Fbuffer_hash): Improve doc string. diff --git a/src/fns.c b/src/fns.c index 2314b4699e..b800f1c47f 100644 --- a/src/fns.c +++ b/src/fns.c @@ -5409,7 +5409,14 @@ of the other hash types instead, e.g. sha256 or sha512. */) DEFUN ("buffer-hash", Fbuffer_hash, Sbuffer_hash, 0, 1, 0, doc: /* Return a hash of the contents of BUFFER-OR-NAME. This hash is performed on the raw internal format of the buffer, -disregarding any coding systems. If nil, use the current buffer. */ ) +disregarding any coding systems. If nil, use the current buffer. + +This function is useful for comparing two buffers running in the same +Emacs, but is not guaranteed to return the same hash between different +Emacs versions. + +It should not be used for anything security-related. See +`secure-hash' for these applications. */ ) (Lisp_Object buffer_or_name) { Lisp_Object buffer; commit 2879c3ec1b91bcf3276c979155dd05494de20a0d Author: Juri Linkov Date: Sun Sep 22 02:00:01 2019 +0300 Support rectangular regions in capitalize-region and capitalize-dwim. * lisp/simple.el (capitalize-dwim): Add arg region-noncontiguous-p in capitalize-region call. * src/casefiddle.c (Fcapitalize_region): Add arg region-noncontiguous-p. If non-nil, operate on multiple chunks. (Bug#37477) (Fdowncase_region): Use builtin symbol Qregion_extract_function rather than calling intern. diff --git a/etc/NEWS b/etc/NEWS index 6a4a6e60d4..20d2e62bd3 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -487,8 +487,8 @@ interface that's more like functions like 'search-forward'. --- ** More commands support noncontiguous rectangular regions, namely -'upcase-dwim', 'downcase-dwim', 'replace-string', 'replace-regexp', -and 'delimit-columns-region'. +'upcase-dwim', 'downcase-dwim', 'capitalize-dwim', 'capitalize-region', +'replace-string', 'replace-regexp', and 'delimit-columns-region'. +++ ** When asked to visit a large file, Emacs now offers visiting it literally. diff --git a/lisp/simple.el b/lisp/simple.el index a267200aeb..31e3b2bbab 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -9074,7 +9074,7 @@ Otherwise, it calls `capitalize-word', with prefix argument passed to it to capitalize ARG words." (interactive "*p") (if (use-region-p) - (capitalize-region (region-beginning) (region-end)) + (capitalize-region (region-beginning) (region-end) (region-noncontiguous-p)) (capitalize-word arg))) ;;; Accessors for `decode-time' values. diff --git a/src/casefiddle.c b/src/casefiddle.c index ee292dda9b..3a1724b306 100644 --- a/src/casefiddle.c +++ b/src/casefiddle.c @@ -556,7 +556,7 @@ point and the mark is operated on. */) if (!NILP (region_noncontiguous_p)) { - bounds = call1 (Fsymbol_value (intern ("region-extract-function")), + bounds = call1 (Fsymbol_value (Qregion_extract_function), intern ("bounds")); while (CONSP (bounds)) @@ -571,15 +571,31 @@ point and the mark is operated on. */) return Qnil; } -DEFUN ("capitalize-region", Fcapitalize_region, Scapitalize_region, 2, 2, "r", +DEFUN ("capitalize-region", Fcapitalize_region, Scapitalize_region, 2, 3, + "(list (region-beginning) (region-end) (region-noncontiguous-p))", doc: /* Convert the region to capitalized form. This means that each word's first character is converted to either title case or upper case, and the rest to lower case. In programs, give two arguments, the starting and ending character positions to operate on. */) - (Lisp_Object beg, Lisp_Object end) + (Lisp_Object beg, Lisp_Object end, Lisp_Object region_noncontiguous_p) { - casify_region (CASE_CAPITALIZE, beg, end); + Lisp_Object bounds = Qnil; + + if (!NILP (region_noncontiguous_p)) + { + bounds = call1 (Fsymbol_value (Qregion_extract_function), + intern ("bounds")); + + while (CONSP (bounds)) + { + casify_region (CASE_CAPITALIZE, XCAR (XCAR (bounds)), XCDR (XCAR (bounds))); + bounds = XCDR (bounds); + } + } + else + casify_region (CASE_CAPITALIZE, beg, end); + return Qnil; } commit a34216351f28dec2e592ce169eabea55715930f8 Author: Eric Abrahamsen Date: Sat Sep 21 14:39:43 2019 -0700 Use eieio-object-p, not obsolete object-p Continued fixes for a81223aeaa * lisp/gnus/gnus-registry.el (gnus-registry-article-marks-to-names, gnus-registry-article-marks-to-chars): object-p is obsolete. diff --git a/lisp/gnus/gnus-registry.el b/lisp/gnus/gnus-registry.el index cc932956f5..a16017ff6d 100644 --- a/lisp/gnus/gnus-registry.el +++ b/lisp/gnus/gnus-registry.el @@ -1007,7 +1007,7 @@ Uses `gnus-registry-marks' to find what shortcuts to install." ;; (defalias 'gnus-user-format-function-M 'gnus-registry-article-marks-to-chars) (defun gnus-registry-article-marks-to-chars (headers) "Show the marks for an article by the :char property." - (if (object-p gnus-registry-db) + (if (eieio-object-p gnus-registry-db) (let* ((id (mail-header-message-id headers)) (marks (when id (gnus-registry-get-id-key id 'mark)))) (concat (delq nil @@ -1023,7 +1023,7 @@ Uses `gnus-registry-marks' to find what shortcuts to install." ;; (defalias 'gnus-user-format-function-M 'gnus-registry-article-marks-to-names) (defun gnus-registry-article-marks-to-names (headers) "Show the marks for an article by name." - (if (object-p gnus-registry-db) + (if (eieio-object-p gnus-registry-db) (let* ((id (mail-header-message-id headers)) (marks (when id (gnus-registry-get-id-key id 'mark)))) (mapconcat (lambda (mark) (symbol-name mark)) marks ",")) commit b86bc62ca5c83afb53b93fccabe3dbfd30d5f6ce Author: Stefan Kangas Date: Sun Sep 15 17:41:27 2019 +0200 Several doc fixes in package.el * lisp/emacs-lisp/package.el (top-level) (package-check-signature, package--from-builtin) (package-desc-full-name, package-desc-suffix) (package-desc--keywords, package--bi-desc) (package-process-define-package, package-archive-base) (package-install-from-archive, package-install-from-buffer) (package-install-file, package-autoremove, describe-package-1) (package-install-button-action, package-delete-button-action) (package-keyword-button-action, package-make-button) (package--print-email-button, package-list-unversioned) (package--emacs-version-list, package-menu-toggle-hiding) (package-hidden-regexps, package-menu-hide-package) (package-menu-get-status, package-menu--find-upgrades) (package-menu--post-refresh): Doc fixes. (Bug#37410) diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el index 409dfedb74..a8362cb205 100644 --- a/lisp/emacs-lisp/package.el +++ b/lisp/emacs-lisp/package.el @@ -120,9 +120,9 @@ ;; - "installed" instead of a blank in the status column ;; - tramp needs its files to be compiled in a certain order. ;; how to handle this? fix tramp? -;; - maybe we need separate .elc directories for various emacs versions -;; and also emacs-vs-xemacs. That way conditional compilation can -;; work. But would this break anything? +;; - maybe we need separate .elc directories for various emacs +;; versions. That way conditional compilation can work. But would +;; this break anything? ;; - William Xu suggests being able to open a package file without ;; installing it ;; - Interface with desktop.el so that restarting after an install @@ -357,9 +357,9 @@ contents of the archive." (defun package-check-signature () "Check whether we have a usable OpenPGP configuration. -If true, and `package-check-signature' is `allow-unsigned', -return `allow-unsigned', otherwise return the value of -`package-check-signature'." +If so, and variable `package-check-signature' is +`allow-unsigned', return `allow-unsigned', otherwise return the +value of variable `package-check-signature'." (if (eq package-check-signature 'allow-unsigned) (progn (require 'epg-config) @@ -475,6 +475,8 @@ Slots: signed) (defun package--from-builtin (bi-desc) + "Create a `package-desc' object from BI-DESC. +BI-DESC should be a `package--bi-desc' object." (package-desc-create :name (pop bi-desc) :version (package--bi-desc-version bi-desc) :summary (package--bi-desc-summary bi-desc) @@ -512,11 +514,21 @@ This is, approximately, the inverse of `version-to-list'. (apply #'concat (nreverse str-list))))) (defun package-desc-full-name (pkg-desc) + "Return full name of package-desc object PKG-DESC. +This is the name of the package with its version appended." (format "%s-%s" (package-desc-name pkg-desc) (package-version-join (package-desc-version pkg-desc)))) (defun package-desc-suffix (pkg-desc) + "Return file-name extension of package-desc object PKG-DESC. +Depending on the `package-desc-kind' of PKG-DESC, this is one of: + + 'single - \".el\" + 'tar - \".tar\" + 'dir - \"\" + +Signal an error if the kind is none of the above." (pcase (package-desc-kind pkg-desc) ('single ".el") ('tar ".tar") @@ -524,6 +536,10 @@ This is, approximately, the inverse of `version-to-list'. (kind (error "Unknown package kind: %s" kind)))) (defun package-desc--keywords (pkg-desc) + "Return keywords of package-desc object PKG-DESC. +These keywords come from the foo-pkg.el file, and in general +corresponds to the keywords in the \"Keywords\" header of the +package." (let ((keywords (cdr (assoc :keywords (package-desc-extras pkg-desc))))) (if (eq (car-safe keywords) 'quote) (nth 1 keywords) @@ -533,10 +549,10 @@ This is, approximately, the inverse of `version-to-list'. "Return the priority of the archive of package-desc object P." (package-archive-priority (package-desc-archive p))) -;; Package descriptor format used in finder-inf.el and package--builtins. (cl-defstruct (package--bi-desc (:constructor package-make-builtin (version summary)) (:type vector)) + "Package descriptor format used in finder-inf.el and package--builtins." version reqs summary) @@ -578,7 +594,15 @@ loaded and/or activated, customize `package-load-list'.") ;; The following functions are called on each installed package by ;; `package-load-all-descriptors', which ultimately populates the ;; `package-alist' variable. + (defun package-process-define-package (exp) + "Process define-package expression EXP and push it to `package-alist'. +EXP should be a form read from a foo-pkg.el file. +Convert EXP into a `package-desc' object using the +`package-desc-from-define' constructor before pushing it to +`package-alist'. +If there already exists a package by that name in +`package-alist', replace that definition with the new one." (when (eq (car-safe exp) 'define-package) (let* ((new-pkg-desc (apply #'package-desc-from-define (cdr exp))) (name (package-desc-name new-pkg-desc)) @@ -869,6 +893,7 @@ untar into a directory named DIR; otherwise, signal an error." (mapcar #'macroexp-quote (apply #'nconc (mapcar (lambda (pair) (list (car pair) (cdr pair))) alist)))) + (defun package-unpack (pkg-desc) "Install the contents of the current buffer as a package." (let* ((name (package-desc-name pkg-desc)) @@ -1902,12 +1927,13 @@ if all the in-between dependencies are also in PACKAGE-LIST." ;; installed in a variety of ways (archives, buffer, file), but ;; requirements (dependencies) are always satisfied by looking in ;; `package-archive-contents'. + (defun package-archive-base (desc) - "Return the archive containing the package NAME." + "Return the package described by DESC." (cdr (assoc (package-desc-archive desc) package-archives))) (defun package-install-from-archive (pkg-desc) - "Download and install a tar package." + "Download and install a tar package defined by PKG-DESC." ;; This won't happen, unless the archive is doing something wrong. (when (eq (package-desc-kind pkg-desc) 'dir) (error "Can't install directory package from archive")) @@ -2084,7 +2110,7 @@ Downloads and installs required packages as needed." ;;;###autoload (defun package-install-file (file) - "Install a package from a file. + "Install a package from FILE. The file can either be a tar file, an Emacs Lisp file, or a directory." (interactive "fPackage file name: ") @@ -2220,7 +2246,7 @@ object." ;;;###autoload (defun package-autoremove () - "Remove packages that are no more needed. + "Remove packages that are no longer needed. Packages that are no more needed by other packages in `package-selected-packages' and their dependencies @@ -2337,6 +2363,8 @@ The description is read from the installed package files." ))) (defun describe-package-1 (pkg) + "Insert the package description for PKG. +Helper function for `describe-package'." (require 'lisp-mnt) (let* ((desc (or (if (package-desc-p pkg) pkg) @@ -2566,6 +2594,9 @@ The description is read from the installed package files." (browse-url-add-buttons)))) (defun package-install-button-action (button) + "Run `package-install' on the package BUTTON points to. +Used for the 'action property of buttons in the buffer created by +`describe-package'." (let ((pkg-desc (button-get button 'package-desc))) (when (y-or-n-p (format-message "Install package `%s'? " (package-desc-full-name pkg-desc))) @@ -2574,6 +2605,9 @@ The description is read from the installed package files." (goto-char (point-min))))) (defun package-delete-button-action (button) + "Run `package-delete' on the package BUTTON points to. +Used for the 'action property of buttons in the buffer created by +`describe-package'." (let ((pkg-desc (button-get button 'package-desc))) (when (y-or-n-p (format-message "Delete package `%s'? " (package-desc-full-name pkg-desc))) @@ -2582,10 +2616,17 @@ The description is read from the installed package files." (goto-char (point-min))))) (defun package-keyword-button-action (button) + "Show filtered \"*Packages*\" buffer for BUTTON. +The buffer is filtered by the `package-keyword' property of BUTTON. +Used for the 'action property of buttons in the buffer created by +`describe-package'." (let ((pkg-keyword (button-get button 'package-keyword))) (package-show-package-list t (list pkg-keyword)))) -(defun package-make-button (text &rest props) +(defun package-make-button (text &rest properties) + "Insert button labeled TEXT with button PROPERTIES at point. +PROPERTIES are passed to `insert-text-button', for which this +function is a convenience wrapper used by `describe-package-1'." (let ((button-text (if (display-graphic-p) text (concat "[" text "]"))) (button-face (if (display-graphic-p) '(:box (:line-width 2 :color "dark grey") @@ -2593,20 +2634,23 @@ The description is read from the installed package files." :foreground "black") 'link))) (apply #'insert-text-button button-text 'face button-face 'follow-link t - props))) - -(defun package--print-email-button (name) - (when (car name) - (insert (car name))) - (when (and (car name) (cdr name)) + properties))) + +(defun package--print-email-button (recipient) + "Insert a button whose action will send an email to RECIPIENT. +NAME should have the form (FULLNAME . EMAIL) where FULLNAME is +either a full name or nil, and EMAIL is a valid email address." + (when (car recipient) + (insert (car recipient))) + (when (and (car recipient) (cdr recipient)) (insert " ")) - (when (cdr name) + (when (cdr recipient) (insert "<") - (insert-text-button (cdr name) + (insert-text-button (cdr recipient) 'follow-link t 'action (lambda (_) (compose-mail - (format "%s <%s>" (car name) (cdr name))))) + (format "%s <%s>" (car recipient) (cdr recipient))))) (insert ">")) (insert "\n")) @@ -2704,13 +2748,13 @@ package PKG-DESC, add one. The alist is keyed with PKG-DESC." (push (cons ,pkg-desc ,status) ,listname))) (defvar package-list-unversioned nil - "If non-nil include packages that don't have a version in `list-package'.") + "If non-nil, include packages that don't have a version in `list-packages'.") (defvar package-list-unsigned nil "If non-nil, mention in the list which packages were installed w/o signature.") (defvar package--emacs-version-list (version-to-list emacs-version) - "`emacs-version', as a list.") + "The value of variable `emacs-version' as a list.") (defun package--incompatible-p (pkg &optional shallow) "Return non-nil if PKG has no chance of being installable. @@ -2785,7 +2829,7 @@ Can be toggled with \\ \\[package-menu-toggle-hiding]. Installed obsolete packages are always displayed.") (defun package-menu-toggle-hiding () - "Toggle visibility of obsolete available packages." + "In Package Menu, toggle visibility of obsolete available packages." (interactive) (unless (derived-mode-p 'package-menu-mode) (user-error "The current buffer is not a Package Menu")) @@ -3103,7 +3147,7 @@ user-error if there is already a refresh running asynchronously." (package-refresh-contents package-menu-async)) (defun package-menu-hide-package () - "Hide a package under point. + "Hide a package under point in Package Menu. If optional arg BUTTON is non-nil, describe its associated package." (interactive) (declare (interactive-only "change `package-hidden-regexps' instead.")) @@ -3202,6 +3246,7 @@ The full list of keys can be viewed with \\[describe-mode]." 'package-menu-view-commentary 'package-menu-describe-package "24.1") (defun package-menu-get-status () + "Return status text of package at point in Package Menu." (let* ((id (tabulated-list-get-id)) (entry (and id (assoc id tabulated-list-entries)))) (if entry @@ -3227,6 +3272,10 @@ consideration." (package-desc-version pkg-desc))) (defun package-menu--find-upgrades () + "In Package Menu, return an alist of packages that can be upgraded. +The alist has the same form as `package-alist', namely a list +of (PKG . DESCS), but where DESCS is the `package-desc' object +corresponding to the newer version." (let (installed available upgrades) ;; Build list of installed/available packages in this buffer. (dolist (entry tabulated-list-entries) @@ -3490,7 +3539,7 @@ Store this list in `package-menu--new-package-list'." (defun package-menu--post-refresh () - "If there's a *Packages* buffer, revert it and check for new packages and upgrades. + "Revert \"*Packages*\" buffer and check for new packages and upgrades. Do nothing if there's no *Packages* buffer. This function is called after `package-refresh-contents' and it commit d49d6ea9677eea1d30aae4244934b1c7336e35a3 Author: Paul Eggert Date: Sat Sep 21 11:27:46 2019 -0700 Revert too-picky file-access tests Problem reported by Andreas Schwab (Bug#37475). * doc/lispref/files.texi (Writing to Files) (Testing Accessibility, Kinds of Files): Document that accessibility and file-type predicates return nil if there is trouble determining accessibility or type. * etc/NEWS: Adjust, and list the affected primitives. * src/callproc.c (init_callproc): Go back to Ffile_exists_p. * src/fileio.c (PICKY_EACCES, file_test_errno): Remove. All uses removed. (Ffile_name_case_insensitive_p, Ffile_exists_p, Ffile_symlink_p) (Ffile_directory_p, Ffile_regular_p): Document that these functions return nil if there is trouble. (Ffile_name_case_insensitive_p, check_file_access) (Ffile_writable_p, Ffile_symlink_p, Ffile_directory_p) (Ffile_accessible_directory_p, Ffile_regular_p) * src/lread.c (Fload): Go back to treating trouble in determining the answer as if the file were missing. * src/fileio.c (Ffile_newer_than_file_p): Use file_attribute_errno not file_test_errno, since returning nil is not appropriate when there are two files to test; e.g., in the rare cases where both file timestamps have overflowed then neither t nor nil is correct. diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index fba9622fec..3746c6d2c9 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -607,8 +607,7 @@ This function appends the contents of the region delimited by @var{filename}. If that file does not exist, it is created. This function returns @code{nil}. -An error is signaled if @var{filename} specifies a nonwritable file, -or a nonexistent file in a directory where files cannot be created. +An error is signaled if you cannot write or create @var{filename}. When called from Lisp, this function is completely equivalent to: @@ -851,12 +850,13 @@ permissions. @defun file-exists-p filename This function returns @code{t} if a file named @var{filename} appears to exist. This does not mean you can necessarily read the file, only -that you can find out its attributes. (On GNU and other POSIX-like +that you can probably find out its attributes. (On GNU and other POSIX-like systems, this is true if the file exists and you have execute permission on the containing directories, regardless of the permissions of the file itself.) -If the file does not exist, this function returns @code{nil}. +If the file does not exist, or if there was trouble determining +whether the file exists, this function returns @code{nil}. Directories are files, so @code{file-exists-p} can return @code{t} when given a directory. However, because @code{file-exists-p} follows @@ -881,7 +881,7 @@ inside the directory, and open those files if their modes permit. This function returns @code{t} if the file @var{filename} can be written or created by you, and @code{nil} otherwise. A file is writable if the file exists and you can write it. It is creatable if it does not exist, -but the specified directory does exist and you can write in that +but its parent directory does exist and you can write in that directory. In the example below, @file{foo} is not writable because the parent @@ -899,7 +899,7 @@ directory. @defun file-accessible-directory-p dirname This function returns @code{t} if you have permission to open existing files in the directory whose name as a file is @var{dirname}; -otherwise (or if there is no such directory), it returns @code{nil}. +otherwise (e.g., if there is no such directory), it returns @code{nil}. The value of @var{dirname} may be either a directory name (such as @file{/foo/}) or the file name of a file which is a directory (such as @file{/foo}, without the final slash). @@ -914,8 +914,8 @@ file in @file{/foo/} will give an error: @end defun @defun access-file filename string -This function opens file @var{filename} for reading, then closes it and -returns @code{nil}. However, if the open fails, it signals an error +If you can read @var{filename} this function returns @code{nil}; +otherwise it signals an error using @var{string} as the error message text. @end defun @@ -1011,6 +1011,7 @@ absolute file name of the target; determining the full file name that the link points to is nontrivial, see below.) If the file @var{filename} is not a symbolic link, or does not exist, +or if there is trouble determining whether it is a symbolic link, @code{file-symlink-p} returns @code{nil}. Here are a few examples of using this function: @@ -1071,7 +1072,9 @@ link. If you actually need the file name of the link target, use @defun file-directory-p filename This function returns @code{t} if @var{filename} is the name of an -existing directory, @code{nil} otherwise. +existing directory. It returns @code{nil} if @var{filename} does +not name a directory, or if there is trouble determining whether +it is a directory. This function follows symbolic links. @example @@ -1103,6 +1106,8 @@ This function follows symbolic links. This function returns @code{t} if the file @var{filename} exists and is a regular file (not a directory, named pipe, terminal, or other I/O device). +It returns @code{nil} if @var{filename} does not exist or is not a regular +file, or if there is trouble determining whether it is a regular file. This function follows symbolic links. @end defun diff --git a/etc/NEWS b/etc/NEWS index cb04da189e..6a4a6e60d4 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2029,11 +2029,14 @@ longer defaults to 'buffer-file-name'. ** File metadata primitives now signal an error if I/O, access, or other serious errors prevent them from determining the result. Formerly, these functions often (though not always) returned nil. -For example, if searching /etc/firewalld results in an I/O error, -(file-symlink-p "/etc/firewalld/firewalld.conf") now signals an error -instead of returning nil, because file-symlink-p cannot determine -whether a symbolic link exists there. These functions still behave as -before if the only problem is that the file does not exist. +For example, if there is an access error, I/O error or low-level +integer overflow when getting the attributes of a file F, +(file-attributes F) now signals an error instead of returning nil. +These functions still behave as before if the only problem is that the +file does not exist. The affected primitives are +directory-files-and-attributes, file-acl, file-attributes, file-modes, +file-newer-than-file-p, file-selinux-context, file-system-info, and +set-visited-file-modtime. --- ** The function 'eldoc-message' now accepts a single argument. diff --git a/src/callproc.c b/src/callproc.c index dbbf15c792..007465cd40 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -1567,12 +1567,12 @@ init_callproc (void) tem = Fexpand_file_name (build_string ("NEWS"), Vdata_directory); if (!NILP (Fequal (srcdir, Vinvocation_directory)) - || !file_access_p (SSDATA (tem), F_OK)) + || NILP (Ffile_exists_p (tem))) { Lisp_Object newdir; newdir = Fexpand_file_name (build_string ("../etc/"), lispdir); tem = Fexpand_file_name (build_string ("NEWS"), newdir); - if (file_access_p (SSDATA (tem), F_OK)) + if (!NILP (Ffile_exists_p (tem))) Vdata_directory = newdir; } } diff --git a/src/fileio.c b/src/fileio.c index b2896c1fe1..b510d48dba 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -253,30 +253,6 @@ file_attribute_errno (Lisp_Object file, int err) return file_metadata_errno ("Getting attributes", file, err); } -/* In theory, EACCES errors for predicates like file-readable-p should - be checked further because they may be problems with an ancestor - directory instead of with the file itself, which means that we - don't have reliable info about the requested file. In practice, - though, DOS_NT platforms set errno to EACCES for missing files like - "/var/mail", so signaling EACCES errors would be a mistake there. - So return nil for EACCES unless PICKY_EACCES, which is false by - default on DOS_NT. */ -#ifndef PICKY_EACCES -# ifdef DOS_NT -enum { PICKY_EACCES = false }; -# else -enum { PICKY_EACCES = true }; -# endif -#endif - -Lisp_Object -file_test_errno (Lisp_Object file, int err) -{ - if (!PICKY_EACCES && err == EACCES) - return Qnil; - return file_metadata_errno ("Testing file", file, err); -} - void close_file_unwind (int fd) { @@ -2453,7 +2429,9 @@ file_name_case_insensitive_err (Lisp_Object file) DEFUN ("file-name-case-insensitive-p", Ffile_name_case_insensitive_p, Sfile_name_case_insensitive_p, 1, 1, 0, doc: /* Return t if file FILENAME is on a case-insensitive filesystem. -The arg must be a string. */) +Return nil if FILENAME does not exist or is not on a case-insensitive +filesystem, or if there was trouble determining whether the filesystem +is case-insensitive. */) (Lisp_Object filename) { Lisp_Object handler; @@ -2467,19 +2445,16 @@ The arg must be a string. */) if (!NILP (handler)) return call2 (handler, Qfile_name_case_insensitive_p, filename); - /* If the file doesn't exist, move up the filesystem tree until we - reach an existing directory or the root. */ + /* If the file doesn't exist or there is trouble checking its + filesystem, move up the filesystem tree until we reach an + existing, trouble-free directory or the root. */ while (true) { int err = file_name_case_insensitive_err (filename); - switch (err) - { - case -1: return Qt; - default: return file_test_errno (filename, err); - case ENOENT: case ENOTDIR: break; - } + if (err <= 0) + return err < 0 ? Qt : Qnil; Lisp_Object parent = file_name_directory (filename); - /* Avoid infinite loop if the root is reported as non-existing + /* Avoid infinite loop if the root has trouble (impossible?). */ if (!NILP (Fstring_equal (parent, filename))) return Qnil; @@ -2739,8 +2714,7 @@ file_name_absolute_p (char const *filename) } /* Return t if FILE exists and is accessible via OPERATION and AMODE, - nil (setting errno) if not. Signal an error if the result cannot - be determined. */ + nil (setting errno) if not. */ static Lisp_Object check_file_access (Lisp_Object file, Lisp_Object operation, int amode) @@ -2758,22 +2732,13 @@ check_file_access (Lisp_Object file, Lisp_Object operation, int amode) } char *encoded_file = SSDATA (ENCODE_FILE (file)); - bool ok = file_access_p (encoded_file, amode); - if (ok) - return Qt; - int err = errno; - if (err == EROFS || err == ETXTBSY - || (PICKY_EACCES && err == EACCES && amode != F_OK - && file_access_p (encoded_file, F_OK))) - { - errno = err; - return Qnil; - } - return file_test_errno (file, err); + return file_access_p (encoded_file, amode) ? Qt : Qnil; } DEFUN ("file-exists-p", Ffile_exists_p, Sfile_exists_p, 1, 1, 0, doc: /* Return t if file FILENAME exists (whether or not you can read it). +Return nil if FILENAME does not exist, or if there was trouble +determining whether the file exists. See also `file-readable-p' and `file-attributes'. This returns nil for a symlink to a nonexistent file. Use `file-symlink-p' to test for such links. */) @@ -2834,16 +2799,7 @@ DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0, should check ACLs though, which do affect this. */ return file_directory_p (encoded) ? Qt : Qnil; #else - if (file_access_p (SSDATA (encoded), W_OK | X_OK)) - return Qt; - int err = errno; - if (err == EROFS - || (err == EACCES && file_access_p (SSDATA (encoded), F_OK))) - { - errno = err; - return Qnil; - } - return file_test_errno (absname, err); + return file_access_p (SSDATA (encoded), W_OK | X_OK) ? Qt : Qnil; #endif } @@ -2919,7 +2875,8 @@ check_emacs_readlinkat (int fd, Lisp_Object file, char const *encoded_file) DEFUN ("file-symlink-p", Ffile_symlink_p, Sfile_symlink_p, 1, 1, 0, doc: /* Return non-nil if file FILENAME is the name of a symbolic link. The value is the link target, as a string. -Otherwise it returns nil. +Return nil if FILENAME does not exist or is not a symbolic link, +of there was trouble determining whether the file is a symbolic link. This function does not check whether the link target exists. */) (Lisp_Object filename) @@ -2935,12 +2892,13 @@ This function does not check whether the link target exists. */) if (!NILP (handler)) return call2 (handler, Qfile_symlink_p, filename); - return check_emacs_readlinkat (AT_FDCWD, filename, - SSDATA (ENCODE_FILE (filename))); + return emacs_readlinkat (AT_FDCWD, SSDATA (ENCODE_FILE (filename))); } DEFUN ("file-directory-p", Ffile_directory_p, Sfile_directory_p, 1, 1, 0, doc: /* Return t if FILENAME names an existing directory. +Return nil if FILENAME does not name a directory, or if there +was trouble determining whether FILENAME is a directory. Symbolic links to directories count as directories. See `file-symlink-p' to distinguish symlinks. */) (Lisp_Object filename) @@ -2953,9 +2911,7 @@ See `file-symlink-p' to distinguish symlinks. */) if (!NILP (handler)) return call2 (handler, Qfile_directory_p, absname); - if (file_directory_p (absname)) - return Qt; - return file_test_errno (absname, errno); + return file_directory_p (absname) ? Qt : Qnil; } /* Return true if FILE is a directory or a symlink to a directory. @@ -3040,12 +2996,7 @@ really is a readable and searchable directory. */) } Lisp_Object encoded_absname = ENCODE_FILE (absname); - if (file_accessible_directory_p (encoded_absname)) - return Qt; - int err = errno; - if (err == EACCES && file_access_p (SSDATA (encoded_absname), F_OK)) - return Qnil; - return file_test_errno (absname, err); + return file_accessible_directory_p (encoded_absname) ? Qt : Qnil; } /* If FILE is a searchable directory or a symlink to a @@ -3108,6 +3059,8 @@ file_accessible_directory_p (Lisp_Object file) DEFUN ("file-regular-p", Ffile_regular_p, Sfile_regular_p, 1, 1, 0, doc: /* Return t if FILENAME names a regular file. This is the sort of file that holds an ordinary stream of data bytes. +Return nil if FILENAME does not exist or is not a regular file, +or there was trouble determining whether FILENAME is a regular file. Symbolic links to regular files count as regular files. See `file-symlink-p' to distinguish symlinks. */) (Lisp_Object filename) @@ -3133,9 +3086,7 @@ See `file-symlink-p' to distinguish symlinks. */) Vw32_get_true_file_attributes = true_attributes; #endif - if (stat_result == 0) - return S_ISREG (st.st_mode) ? Qt : Qnil; - return file_test_errno (absname, errno); + return stat_result == 0 && S_ISREG (st.st_mode) ? Qt : Qnil; } DEFUN ("file-selinux-context", Ffile_selinux_context, @@ -3541,20 +3492,15 @@ otherwise, if FILE2 does not exist, the answer is t. */) { err1 = errno; if (err1 != EOVERFLOW) - return file_test_errno (absname1, err1); + return file_attribute_errno (absname1, err1); } - if (stat (SSDATA (ENCODE_FILE (absname2)), &st2) != 0) { - file_test_errno (absname2, errno); + file_attribute_errno (absname2, errno); return Qt; } - if (err1) - { - file_test_errno (absname1, err1); - eassume (false); - } + file_attribute_errno (absname1, err1); return (timespec_cmp (get_stat_mtime (&st2), get_stat_mtime (&st1)) < 0 ? Qt : Qnil); diff --git a/src/lisp.h b/src/lisp.h index b081ae1cee..e68d2732e2 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4315,7 +4315,6 @@ extern AVOID report_file_errno (const char *, Lisp_Object, int); extern AVOID report_file_error (const char *, Lisp_Object); extern AVOID report_file_notify_error (const char *, Lisp_Object); extern Lisp_Object file_attribute_errno (Lisp_Object, int); -extern Lisp_Object file_test_errno (Lisp_Object, int); extern bool internal_delete_file (Lisp_Object); extern Lisp_Object check_emacs_readlinkat (int, Lisp_Object, char const *); extern bool file_directory_p (Lisp_Object); diff --git a/src/lread.c b/src/lread.c index 4f3446b09d..151731a81d 100644 --- a/src/lread.c +++ b/src/lread.c @@ -1343,26 +1343,18 @@ Return t if the file exists and loads successfully. */) /* openp already checked for newness, no point doing it again. FIXME would be nice to get a message when openp ignores suffix order due to load_prefer_newer. */ - Lisp_Object notfound = found; if (!load_prefer_newer && is_elc) { result = stat (SSDATA (efound), &s1); - int err = errno; if (result == 0) { SSET (efound, SBYTES (efound) - 1, 0); result = stat (SSDATA (efound), &s2); - err = errno; SSET (efound, SBYTES (efound) - 1, 'c'); - if (result != 0) - notfound = Fsubstring (found, make_fixnum (0), - make_fixnum (-1)); } - if (result != 0) - file_test_errno (notfound, err); - else if (timespec_cmp (get_stat_mtime (&s1), - get_stat_mtime (&s2)) - < 0) + + if (result == 0 + && timespec_cmp (get_stat_mtime (&s1), get_stat_mtime (&s2)) < 0) { /* Make the progress messages mention that source is newer. */ newer = 1; commit 56213b15e65a350f3f8cd9426a97691d8ff217ee Author: Eli Zaretskii Date: Sat Sep 21 21:33:04 2019 +0300 ; * etc/NEWS: Tweak recently added entries. diff --git a/etc/NEWS b/etc/NEWS index b120b5a817..cb04da189e 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2567,11 +2567,13 @@ left to higher-level functions. ** Image mode -*** An Exif library has been added that can parse JPEG files and +*** New library Exif. +An Exif library has been added that can parse JPEG files and output data about creation times and orientation and the like. 'exif-parse' is the main interface function. -*** 'image-mode' started using ImageMagick by default for all images +*** 'imagemagick-types-inhibit' disables using ImageMagick by default. +'image-mode' started using ImageMagick by default for all images some years back. It now respects 'imagemagick-types-inhibit' as a way to disable that. commit 2f3544362c1fb8212156c2df06363fbb7fdf747b Author: Noam Postavsky Date: Fri Sep 20 09:06:05 2019 -0400 Remove lisp/erc/ file-local indent-tabs-mode settings * lisp/erc/erc-autoaway.el: * lisp/erc/erc-backend.el: * lisp/erc/erc-button.el: * lisp/erc/erc-compat.el: * lisp/erc/erc-dcc.el: * lisp/erc/erc-fill.el: * lisp/erc/erc-ibuffer.el: * lisp/erc/erc-identd.el: * lisp/erc/erc-imenu.el: * lisp/erc/erc-join.el: * lisp/erc/erc-list.el: * lisp/erc/erc-log.el: * lisp/erc/erc-match.el: * lisp/erc/erc-menu.el: * lisp/erc/erc-netsplit.el: * lisp/erc/erc-networks.el: * lisp/erc/erc-notify.el: * lisp/erc/erc-page.el: * lisp/erc/erc-pcomplete.el: * lisp/erc/erc-replace.el: * lisp/erc/erc-ring.el: * lisp/erc/erc-services.el: * lisp/erc/erc-sound.el: * lisp/erc/erc-speedbar.el: * lisp/erc/erc-stamp.el: * lisp/erc/erc-track.el: * lisp/erc/erc-truncate.el: * lisp/erc/erc-xdcc.el: * lisp/erc/erc.el: Remove indent-tabs-mode setting, so that we follow Emacs' global indent-tabs-mode=nil setting (and much of the indentation in these fails is already spaces anyway). Also remove tab-width=8 settings, since those are redundant with the setting in the top-level .dir-locals.el. diff --git a/lisp/erc/erc-autoaway.el b/lisp/erc/erc-autoaway.el index 9e224e0b82..83a738fd07 100644 --- a/lisp/erc/erc-autoaway.el +++ b/lisp/erc/erc-autoaway.el @@ -283,6 +283,4 @@ active server buffer available." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index 8b30834025..3250d0fdfd 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el @@ -2075,6 +2075,3 @@ See `erc-display-error-notice'." nil (provide 'erc-backend) ;;; erc-backend.el ends here -;; Local Variables: -;; indent-tabs-mode: nil -;; End: diff --git a/lisp/erc/erc-button.el b/lisp/erc/erc-button.el index 2dbf13cfdf..14e5204632 100644 --- a/lisp/erc/erc-button.el +++ b/lisp/erc/erc-button.el @@ -531,5 +531,4 @@ and `apropos' for other symbols." ;;; erc-button.el ends here ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: nil ;; End: diff --git a/lisp/erc/erc-compat.el b/lisp/erc/erc-compat.el index aae0f09a2c..1e6005d72e 100644 --- a/lisp/erc/erc-compat.el +++ b/lisp/erc/erc-compat.el @@ -158,6 +158,4 @@ If START or END is negative, it counts from the end." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-dcc.el b/lisp/erc/erc-dcc.el index f3b2336998..f8bd5bf308 100644 --- a/lisp/erc/erc-dcc.el +++ b/lisp/erc/erc-dcc.el @@ -1245,5 +1245,4 @@ other client." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: nil ;; End: diff --git a/lisp/erc/erc-fill.el b/lisp/erc/erc-fill.el index 705c7e69bb..222f742112 100644 --- a/lisp/erc/erc-fill.el +++ b/lisp/erc/erc-fill.el @@ -194,5 +194,4 @@ You can put this on `erc-insert-modify-hook' and/or `erc-send-modify-hook'." ;;; erc-fill.el ends here ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: nil ;; End: diff --git a/lisp/erc/erc-ibuffer.el b/lisp/erc/erc-ibuffer.el index 149c858d2e..ac85e8952b 100644 --- a/lisp/erc/erc-ibuffer.el +++ b/lisp/erc/erc-ibuffer.el @@ -184,8 +184,3 @@ (provide 'erc-ibuffer) ;;; erc-ibuffer.el ends here -;; -;; Local Variables: -;; indent-tabs-mode: t -;; tab-width: 8 -;; End: diff --git a/lisp/erc/erc-identd.el b/lisp/erc/erc-identd.el index d95e0eac0c..836ae78fb6 100644 --- a/lisp/erc/erc-identd.el +++ b/lisp/erc/erc-identd.el @@ -116,6 +116,4 @@ The default port is specified by `erc-identd-port'." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-imenu.el b/lisp/erc/erc-imenu.el index 08f52f1364..3e1455ae5b 100644 --- a/lisp/erc/erc-imenu.el +++ b/lisp/erc/erc-imenu.el @@ -132,6 +132,4 @@ Don't rely on this function, read it first!" ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-join.el b/lisp/erc/erc-join.el index c292fdbd79..006fc6cd51 100644 --- a/lisp/erc/erc-join.el +++ b/lisp/erc/erc-join.el @@ -220,6 +220,4 @@ This function is run from `erc-nickserv-identified-hook'." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-list.el b/lisp/erc/erc-list.el index d8d9e17c95..821734bd8d 100644 --- a/lisp/erc/erc-list.el +++ b/lisp/erc/erc-list.el @@ -226,6 +226,4 @@ to RFC and send the LIST header (#321) at start of list transmission." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-log.el b/lisp/erc/erc-log.el index 2b9a0aae1d..d6763c01f3 100644 --- a/lisp/erc/erc-log.el +++ b/lisp/erc/erc-log.el @@ -456,6 +456,4 @@ You can save every individual message by putting this function on ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-match.el b/lisp/erc/erc-match.el index 092b5953c4..e9ed735516 100644 --- a/lisp/erc/erc-match.el +++ b/lisp/erc/erc-match.el @@ -649,6 +649,4 @@ This function is meant to be called from `erc-text-matched-hook'." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-menu.el b/lisp/erc/erc-menu.el index 17e36984ea..4293c2f8a4 100644 --- a/lisp/erc/erc-menu.el +++ b/lisp/erc/erc-menu.el @@ -137,6 +137,4 @@ ERC menu yet.") ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-netsplit.el b/lisp/erc/erc-netsplit.el index 305fdf9d94..9db0879a13 100644 --- a/lisp/erc/erc-netsplit.el +++ b/lisp/erc/erc-netsplit.el @@ -205,6 +205,4 @@ join from that split has been detected or not.") ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-networks.el b/lisp/erc/erc-networks.el index 3ba5ce4e5e..352f714491 100644 --- a/lisp/erc/erc-networks.el +++ b/lisp/erc/erc-networks.el @@ -873,6 +873,4 @@ VALUE is the options value.") ;;; erc-networks.el ends here ;; ;; Local Variables: -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-notify.el b/lisp/erc/erc-notify.el index 45dae89990..d8fd52af37 100644 --- a/lisp/erc/erc-notify.el +++ b/lisp/erc/erc-notify.el @@ -254,6 +254,4 @@ with args, toggle notify status of people." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-page.el b/lisp/erc/erc-page.el index cb57883ae6..155ae03b3c 100644 --- a/lisp/erc/erc-page.el +++ b/lisp/erc/erc-page.el @@ -108,6 +108,4 @@ receive pages if `erc-page-mode' is on." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-pcomplete.el b/lisp/erc/erc-pcomplete.el index dd2da85d0e..55405498f1 100644 --- a/lisp/erc/erc-pcomplete.el +++ b/lisp/erc/erc-pcomplete.el @@ -285,5 +285,4 @@ up to where point is right now." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: nil ;; End: diff --git a/lisp/erc/erc-replace.el b/lisp/erc/erc-replace.el index 2e0e54a030..80c4eecc80 100644 --- a/lisp/erc/erc-replace.el +++ b/lisp/erc/erc-replace.el @@ -91,6 +91,4 @@ It replaces text according to `erc-replace-alist'." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-ring.el b/lisp/erc/erc-ring.el index c5d62ccfea..c5780484e7 100644 --- a/lisp/erc/erc-ring.el +++ b/lisp/erc/erc-ring.el @@ -147,5 +147,4 @@ containing a password." ;;; erc-ring.el ends here ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: nil ;; End: diff --git a/lisp/erc/erc-services.el b/lisp/erc/erc-services.el index 886ba60eb4..b8eef7f618 100644 --- a/lisp/erc/erc-services.el +++ b/lisp/erc/erc-services.el @@ -454,6 +454,4 @@ When called interactively, read the password using `read-passwd'." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-sound.el b/lisp/erc/erc-sound.el index 34f7ce62c7..fbf9c0d78f 100644 --- a/lisp/erc/erc-sound.el +++ b/lisp/erc/erc-sound.el @@ -146,6 +146,4 @@ See also `play-sound-file'." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-speedbar.el b/lisp/erc/erc-speedbar.el index a1e10ca3a2..a3c5f27999 100644 --- a/lisp/erc/erc-speedbar.el +++ b/lisp/erc/erc-speedbar.el @@ -362,6 +362,4 @@ The INDENT level is ignored." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-stamp.el b/lisp/erc/erc-stamp.el index ee177e3acb..1ddb87ae86 100644 --- a/lisp/erc/erc-stamp.el +++ b/lisp/erc/erc-stamp.el @@ -413,6 +413,4 @@ enabled when the message was inserted." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-track.el b/lisp/erc/erc-track.el index 055c2eb485..0574cbe088 100644 --- a/lisp/erc/erc-track.el +++ b/lisp/erc/erc-track.el @@ -947,6 +947,4 @@ switch back to the last non-ERC buffer visited. Next is defined by ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-truncate.el b/lisp/erc/erc-truncate.el index 0417429552..1c7de364f4 100644 --- a/lisp/erc/erc-truncate.el +++ b/lisp/erc/erc-truncate.el @@ -113,6 +113,4 @@ Meant to be used in hooks, like `erc-insert-post-hook'." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc-xdcc.el b/lisp/erc/erc-xdcc.el index 162b22e15c..5ccbcd4a93 100644 --- a/lisp/erc/erc-xdcc.el +++ b/lisp/erc/erc-xdcc.el @@ -134,6 +134,4 @@ being evaluated and should return strings." ;; ;; Local Variables: ;; generated-autoload-file: "erc-loaddefs.el" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 74376b0cb3..65a4d5034d 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -6854,6 +6854,4 @@ Otherwise, connect to HOST:PORT as USER and /join CHANNEL." ;; ;; Local Variables: ;; outline-regexp: ";;+" -;; indent-tabs-mode: t -;; tab-width: 8 ;; End: commit b5afd295ccac33a18d901fc294944798c3bdfe2d Author: Lars Ingebrigtsen Date: Sat Sep 21 18:27:53 2019 +0200 Fix some commentary typos in exif.el * lisp/image/exif.el (exif--parse-exif-chunk) (exif--parse-directory): Commentary typo fixes. diff --git a/lisp/image/exif.el b/lisp/image/exif.el index 2ec256bb2e..2ceafd5bfc 100644 --- a/lisp/image/exif.el +++ b/lisp/image/exif.el @@ -107,7 +107,7 @@ The return value is a list of Exif items." (delete-region (point-min) (point)) (let* ((endian-marker (exif--read-chunk 2)) (le (cond - ;; "Morotola" is big-endian. + ;; "Motorola" is big-endian. ((equal endian-marker "MM") nil) ;; "Intel" is little-endian. @@ -141,7 +141,7 @@ The return value is a list of Exif items." ;; The actual length is the number in this field ;; times the "inherent" length of the field format ;; (i.e., "long integer" (4 bytes) or "ascii" (1 - ;; byte). + ;; byte)). for length = (* (exif--read-number 4 le) (cdr field-format)) for value = (exif--read-number 4 le) @@ -171,7 +171,7 @@ The return value is a list of Exif items." ;; keep parsing. (progn (goto-char (1+ next)) - (append dir (exif--parse-directory le))) + (nconc dir (exif--parse-directory le))) ;; We've reached the end of the directories. dir)))) commit 535b65875e7e47e1fd6bec1753f687592ae600b8 Author: Lars Ingebrigtsen Date: Sat Sep 21 18:13:05 2019 +0200 Add an Exif parsing library * lisp/image/exif.el: New file (bug#23070). * test/lisp/image/exif-tests.el: Add some basic tests. diff --git a/etc/NEWS b/etc/NEWS index 238ea840dd..b120b5a817 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2567,6 +2567,10 @@ left to higher-level functions. ** Image mode +*** An Exif library has been added that can parse JPEG files and +output data about creation times and orientation and the like. +'exif-parse' is the main interface function. + *** 'image-mode' started using ImageMagick by default for all images some years back. It now respects 'imagemagick-types-inhibit' as a way to disable that. diff --git a/lisp/image/exif.el b/lisp/image/exif.el new file mode 100644 index 0000000000..2ec256bb2e --- /dev/null +++ b/lisp/image/exif.el @@ -0,0 +1,224 @@ +;;; exif.el --- parsing Exif data in JPEG images -*- lexical-binding: t -*- + +;; Copyright (C) 2019 Free Software Foundation, Inc. + +;; Author: Lars Magne Ingebrigtsen +;; Keywords: images + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;; Specification at: + +;; https://www.media.mit.edu/pia/Research/deepview/exif.html +;; but it's kinda er not very easy to read. + +;; The JPEG format is: +;; +;; FFD8 and then any number of chunks on the format: FFxx SSSS ..., +;; where FFxx is the ID, and SSSS is the length of the chunk plus 2. +;; When you get to ID FFDA, the image itself is over and you can stop +;; parsing. +;; +;; The Exif data is in the TIFF format. It starts off with the six +;; bytes "Exif^0^0". +;; +;; Then either "II" or "MM", where "II" means little-endian and "MM" +;; means big-endian. All subsequent numbers should be read in +;; according to this. +;; +;; Next follows two bytes that should always represent 0x2a, and then +;; four bytes that's the offset to where the IFD "image file +;; directory" starts. (It's an offset from the start of this chunk; +;; i.e., where "II"/"MM" is; all offsets in the TIFF format are from +;; this point.) +;; +;; The IFD starts with two bytes that says how many entries there are +;; in the directory, and then that number of entries follows, and then +;; an offset to the next IFD. + +;; Usage: (exif-parse "test.jpg") => +;; ((:tag 274 :tag-name orientation :format 3 :format-type short :value 1) +;; (:tag 282 :tag-name x-resolution :format 5 :format-type rational :value +;; (180 . 1)) +;; (:tag 306 :tag-name date-time :format 2 :format-type ascii +;; :value "2019:09:21 16:22:13") +;; ...) + +;;; Code: + +(require 'cl-lib) + +(defvar exif-tag-alist + '((11 processing-software) + (271 make) + (272 model) + (274 orientation) + (282 x-resolution) + (283 y-resolution) + (296 resolution-unit) + (305 software) + (306 date-time)) + "Alist of tag values and their names.") + +(defun exif-parse (file) + "Parse FILE (a JPEG file) and return the Exif data, if any. +The return value is a list of Exif items." + (when-let ((app1 (cdr (assq #xffe1 (exif--parse-jpeg file))))) + (exif--parse-exif-chunk app1))) + +(defun exif--parse-jpeg (file) + (with-temp-buffer + (set-buffer-multibyte nil) + (insert-file-contents-literally file) + (unless (= (exif--read-number-be 2) #xffd8) ; SOI (start of image) + (error "Not a valid JPEG file")) + (cl-loop for segment = (exif--read-number-be 2) + for size = (exif--read-number-be 2) + ;; Stop parsing when we get to SOS (start of stream); + ;; this is when the image itself starts, and there will + ;; be no more chunks of interest after that. + while (not (= segment #xffda)) + collect (cons segment (exif--read-chunk (- size 2)))))) + +(defun exif--parse-exif-chunk (data) + (with-temp-buffer + (set-buffer-multibyte nil) + (insert data) + (goto-char (point-min)) + ;; The Exif data is in the APP1 JPEG chunk and starts with + ;; "Exif\0\0". + (unless (equal (exif--read-chunk 6) (string ?E ?x ?i ?f ?\0 ?\0)) + (error "Not a valid Exif chunk")) + (delete-region (point-min) (point)) + (let* ((endian-marker (exif--read-chunk 2)) + (le (cond + ;; "Morotola" is big-endian. + ((equal endian-marker "MM") + nil) + ;; "Intel" is little-endian. + ((equal endian-marker "II") + t) + (t + (error "Invalid endian-ness %s" endian-marker))))) + ;; Another magical number. + (unless (= (exif--read-number 2 le) #x002a) + (error "Invalid TIFF header length")) + (let ((offset (exif--read-number 2 le))) + ;; Jump to where the IFD (directory) starts and parse it. + (goto-char (1+ offset)) + (exif--parse-directory le))))) + +(defun exif--field-format (number) + (cl-case number + (1 (cons 'byte 1)) + (2 (cons 'ascii 1)) + (3 (cons 'short 2)) + (4 (cons 'long 4)) + (5 (cons 'rational 8)) + (otherwise (cons 'unknown 1)))) + +(defun exif--parse-directory (le) + (let ((dir + (cl-loop repeat (exif--read-number 2 le) + for tag = (exif--read-number 2 le) + for format = (exif--read-number 2 le) + for field-format = (exif--field-format format) + ;; The actual length is the number in this field + ;; times the "inherent" length of the field format + ;; (i.e., "long integer" (4 bytes) or "ascii" (1 + ;; byte). + for length = (* (exif--read-number 4 le) + (cdr field-format)) + for value = (exif--read-number 4 le) + collect (list :tag tag + :tag-name (cadr (assq tag exif-tag-alist)) + :format format + :format-type (car field-format) + :value (exif--process-value + (if (> length 4) + ;; If the length of the data + ;; is more than 4 bytes, then + ;; it's actually stored after + ;; this directory, and the + ;; value here is just the + ;; offset to use to find the + ;; data. + (buffer-substring + (1+ value) (+ (1+ value) length)) + ;; The value is stored + ;; directly in the directory. + value) + (car field-format) + le))))) + (let ((next (exif--read-number 4 le))) + (if (> next 0) + ;; There's more than one directory; if so, jump to it and + ;; keep parsing. + (progn + (goto-char (1+ next)) + (append dir (exif--parse-directory le))) + ;; We've reached the end of the directories. + dir)))) + +(defun exif--process-value (value type le) + "Do type-based post-processing of the value." + (cl-case type + ;; Chop off trailing zero byte. + ('ascii (substring value 0 (1- (length value)))) + ('rational (with-temp-buffer + (set-buffer-multibyte nil) + (insert value) + (goto-char (point-min)) + (cons (exif--read-number 4 le) + (exif--read-number 4 le)))) + (otherwise value))) + +(defun exif--read-chunk (bytes) + "Return BYTES octets from the buffer and advance point that much." + (prog1 + (buffer-substring (point) (+ (point) bytes)) + (forward-char bytes))) + +(defun exif--read-number-be (bytes) + "Read BYTES octets from the buffer as a chunk of big-endian bytes. +Advance point to after the read bytes." + (let ((sum 0)) + (dotimes (_ bytes) + (setq sum (+ (* sum 256) (following-char))) + (forward-char 1)) + sum)) + +(defun exif--read-number-le (bytes) + "Read BYTES octets from the buffer as a chunk of low-endian bytes. +Advance point to after the read bytes." + (let ((sum 0)) + (dotimes (i bytes) + (setq sum (+ (* (following-char) (expt 256 i)) sum)) + (forward-char 1)) + sum)) + +(defun exif--read-number (bytes lower-endian) + "Read BYTES octets from the buffer with endianness determined by LOWER-ENDIAN. +Advance point to after the read bytes." + (if lower-endian + (exif--read-number-le bytes) + (exif--read-number-be bytes))) + +(provide 'exif) + +;;; exif.el ends here diff --git a/test/data/image/black.jpg b/test/data/image/black.jpg new file mode 100644 index 0000000000..be9af2a9a0 Binary files /dev/null and b/test/data/image/black.jpg differ diff --git a/test/lisp/image/exif-tests.el b/test/lisp/image/exif-tests.el new file mode 100644 index 0000000000..d6b46980d7 --- /dev/null +++ b/test/lisp/image/exif-tests.el @@ -0,0 +1,44 @@ +;;; exif-tests.el --- tests for exif.el -*- lexical-binding: t -*- + +;; Copyright (C) 2019 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Code: + +(require 'ert) +(require 'exif) +(require 'seq) + +(defun test-image-file (name) + (expand-file-name + name (expand-file-name "data/image" + (or (getenv "EMACS_TEST_DIRECTORY") + "../../")))) + +(defun exif-elem (exif elem) + (plist-get (seq-find (lambda (e) + (eq elem (plist-get e :tag-name))) + exif) + :value)) + +(ert-deftest test-exif-parse () + (let ((exif (exif-parse (test-image-file "black.jpg")))) + (should (equal (exif-elem exif 'make) "Panasonic")) + (should (equal (exif-elem exif 'orientation) 1)) + (should (equal (exif-elem exif 'x-resolution) '(180 . 1))))) + +;;; exif-tests.el ends here commit 56985dd8a69fc2729422cf8f95efbd03ee6b021e Author: Alan Mackenzie Date: Sat Sep 21 12:35:34 2019 +0000 CC Mode: Fix wrong fontification of FOO in ASSERT (FOO && !BAR) * lisp/progmodes/cc-engine.el (c-forward-decl-or-cast-1): Don't recognize the construct in CASE 18, unless additionally at-decl-end is set. diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index 4916b1dabb..6d7d322def 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -10100,7 +10100,8 @@ This function might do hidden buffer changes." (throw 'at-decl-or-cast t))))) ;; CASE 18 - (when (and (not (memq context '(nil top))) + (when (and at-decl-end + (not (memq context '(nil top))) (or (and got-prefix (not got-number)) (and (eq context 'decl) (not c-recognize-paren-inits) commit e9724b559ee8548983170155c472aa14792ff2fb Author: Lars Ingebrigtsen Date: Sat Sep 21 12:09:10 2019 +0200 Fix two ` characters in NEWS diff --git a/etc/NEWS b/etc/NEWS index 82b8506ae0..238ea840dd 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1481,7 +1481,7 @@ available for output of asynchronous shell commands. --- *** 'pcomplete/make' now completes on targets in included files, recursively. To recover the previous behavior, set new user option -`pcmpl-gnu-makefile-includes' to nil. +'pcmpl-gnu-makefile-includes' to nil. ** Auth-source @@ -1491,7 +1491,7 @@ To recover the previous behavior, set new user option *** .authinfo and .netrc files now use a new mode: 'authinfo-mode'. This is just like 'fundamental-mode', except that it hides passwords under a "****" display property. When the cursor moves to this text, -the real password is revealed (via `reveal-mode'). +the real password is revealed (via 'reveal-mode'). ** Tramp commit 893111f48abd504208408904ea54bc487641756d Author: Lars Ingebrigtsen Date: Sat Sep 21 12:03:55 2019 +0200 Hide passwords in .authinfo and .netrc files * lisp/auth-source.el (authinfo-mode): New mode (bug#28785). (authinfo--hide-passwords, authinfo--toggle-display): New functions. * lisp/files.el (auto-mode-alist): Use authinfo-mode for .authinfo and .netrc files. diff --git a/etc/NEWS b/etc/NEWS index 02fe93a782..82b8506ae0 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1488,6 +1488,11 @@ To recover the previous behavior, set new user option --- *** The Secret Service backend supports the ':create' key now. +*** .authinfo and .netrc files now use a new mode: 'authinfo-mode'. +This is just like 'fundamental-mode', except that it hides passwords +under a "****" display property. When the cursor moves to this text, +the real password is revealed (via `reveal-mode'). + ** Tramp +++ diff --git a/lisp/auth-source.el b/lisp/auth-source.el index 2164a550b0..9669ae976c 100644 --- a/lisp/auth-source.el +++ b/lisp/auth-source.el @@ -2397,6 +2397,38 @@ MODE can be \"login\" or \"password\"." (setq password (funcall password))) (list user password auth-info))) +;;; Tiny mode for editing .netrc/.authinfo modes (that basically just +;;; hides passwords). + +;;;###autoload +(define-derived-mode authinfo-mode fundamental-mode "Authinfo" + "Mode for editing .authinfo/.netrc files. + +This is just like `fundamental-mode', but hides passwords. The +passwords are revealed when point moved into the password. + +\\{authinfo-mode-map}" + (authinfo--hide-passwords (point-min) (point-max)) + (reveal-mode)) + +(defun authinfo--hide-passwords (start end) + (save-excursion + (save-restriction + (narrow-to-region start end) + (goto-char start) + (while (re-search-forward "\\bpassword +\\([^\n\t ]+\\)" + nil t) + (let ((overlay (make-overlay (match-beginning 1) (match-end 1)))) + (overlay-put overlay 'display (propertize "****" + 'face 'warning)) + (overlay-put overlay 'reveal-toggle-invisible + #'authinfo--toggle-display)))))) + +(defun authinfo--toggle-display (overlay hide) + (if hide + (overlay-put overlay 'display (propertize "****" 'face 'warning)) + (overlay-put overlay 'display nil))) + (provide 'auth-source) ;;; auth-source.el ends here diff --git a/lisp/files.el b/lisp/files.el index 0c3da1fe3c..8c355b02aa 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -2811,6 +2811,7 @@ ARC\\|ZIP\\|LZH\\|LHA\\|ZOO\\|[JEW]AR\\|XPI\\|RAR\\|CBR\\|7Z\\)\\'" . archive-mo ("\\.docbook\\'" . sgml-mode) ("\\.com\\'" . dcl-mode) ("/config\\.\\(?:bat\\|log\\)\\'" . fundamental-mode) + ("/\\.\\(authinfo\\|netrc\\)\\'" . authinfo-mode) ;; Windows candidates may be opened case sensitively on Unix ("\\.\\(?:[iI][nN][iI]\\|[lL][sS][tT]\\|[rR][eE][gG]\\|[sS][yY][sS]\\)\\'" . conf-mode) ("\\.la\\'" . conf-unix-mode) commit 2c7224f8942e6338ea9dce727a7b573bf4f3f5a6 Author: Lars Ingebrigtsen Date: Sat Sep 21 12:00:12 2019 +0200 Allow reveal.el to toggle `display' properties * lisp/reveal.el (reveal-open-new-overlays): Allow also toggling `displa' overlay properties (bug#28785). diff --git a/etc/NEWS b/etc/NEWS index e8d3dffd3b..02fe93a782 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2130,6 +2130,11 @@ valid event type. * Lisp Changes in Emacs 27.1 +** 'reveal-mode' can now also be used for more than to toggle between +invisible and visible: It can also toggle 'display' properties in +overlays. This is only done on 'display' properties that have the +'reveal-toggle-invisible' property set. + +++ ** 'process-contact' now takes an optional NO-BLOCK parameter to allow not waiting for a process to be set up. diff --git a/lisp/reveal.el b/lisp/reveal.el index 67740c8149..5483073474 100644 --- a/lisp/reveal.el +++ b/lisp/reveal.el @@ -26,6 +26,11 @@ ;; is always visible. When point enters a region of hidden text, ;; `reveal-mode' temporarily makes it visible. ;; +;; Overlays can also use the `display' property. For them to be +;; revealed, the `reveal-toggle-invisible' property also has to be +;; present, and should be a function to toggle between having a +;; display property and not. +;; ;; This is normally used in conjunction with `outline-minor-mode', ;; `hs-minor-mode', `hide-ifdef-mode', ... ;; @@ -103,21 +108,32 @@ Each element has the form (WINDOW . OVERLAY).") (overlays-at (point)))) (setq old-ols (delq ol old-ols)) (when (overlay-start ol) ;Check it's still live. - (let ((inv (overlay-get ol 'invisible)) open) - (when (and inv - ;; There's an `invisible' property. Make sure it's - ;; actually invisible, and ellipsized. - (and (consp buffer-invisibility-spec) - (cdr (assq inv buffer-invisibility-spec))) + ;; We either have an invisible overlay, or a display + ;; overlay. Always reveal invisible text, but only reveal + ;; display properties if `reveal-toggle-invisible' is + ;; present. + (let ((inv (overlay-get ol 'invisible)) + (disp (and (overlay-get ol 'display) + (overlay-get ol 'reveal-toggle-invisible))) + open) + (when (and (or (and inv + ;; There's an `invisible' property. + ;; Make sure it's actually invisible, + ;; and ellipsized. + (and (consp buffer-invisibility-spec) + (cdr (assq inv buffer-invisibility-spec)))) + disp) (or (setq open (or (overlay-get ol 'reveal-toggle-invisible) (and (symbolp inv) (get inv 'reveal-toggle-invisible)) - (overlay-get ol 'isearch-open-invisible-temporary))) + (overlay-get + ol 'isearch-open-invisible-temporary))) (overlay-get ol 'isearch-open-invisible) (and (consp buffer-invisibility-spec) - (cdr (assq inv buffer-invisibility-spec)))) - (overlay-put ol 'reveal-invisible inv)) + (cdr (assq inv buffer-invisibility-spec))))) + (when inv + (overlay-put ol 'reveal-invisible inv)) (push (cons (selected-window) ol) reveal-open-spots) (if (null open) (overlay-put ol 'invisible nil) commit 8147d939ae5c2d933c9a87f6281a8dfd7c850836 Author: Eli Zaretskii Date: Sat Sep 21 12:03:31 2019 +0300 ; * etc/TODO: Update. diff --git a/etc/TODO b/etc/TODO index a065763933..ac47b0ad2b 100644 --- a/etc/TODO +++ b/etc/TODO @@ -219,6 +219,54 @@ https://lists.gnu.org/r/emacs-devel/2013-11/msg00515.html processing. That is why we added text properties and variable width fonts. However, more features are still needed to achieve this. +** Support ligatures out of the box +For the list of typographical ligatures, see + + https://en.wikipedia.org/wiki/Orthographic_ligature#Ligatures_in_Unicode_(Latin_alphabets) + +For Text and derived modes, the job is to figure out which ligatures +we want to support, how to let the user customize that, and probably +define a minor mode for automatic ligation (as some contexts might not +want, say, "fi" or "ff" always yield a ligature, and also because it +might slow down redisplay, because character composition goes through +Lisp). + +For ligature support in programming language modes, one can look at +the various add-on packages out there that provide the feature via +prettify-symbols-mode. We need to figure out which ligatures are +needed for each programming language, and provide user options to turn +this on and off. + +The implementation should use the infrastructure for character +compositions, i.e., we should define appropriate regexp-based rules +for character sequences that need to be composed into ligatures, and +populate composition-function-table with those rules. See +composite.el for examples of this, and also grep lisp/language/*.el +for references to composition-function-table. + +The prettify-symbols-mode should be deprecated once ligature support +is in place. + +** Support for Stylistic Sets +This will allow using "alternate glyphs" supported by modern fonts. +For an overview of this feature, see + + https://www.typography.com/faq/157 + https://glyphsapp.com/tutorials/stylistic-sets + +HarfBuzz supports this, see this discussion: + + https://lists.freedesktop.org/archives/harfbuzz/2019-September/007434.html + +One possible way of letting Lisp program support this would be to +introduce a new text property 'stylistic-set' whose value will be the +set name(s), a symbol or a list of symbols. Characters that have this +property should be processed specially by 'get_glyph_face_and_encoding': +instead of calling the 'encode_char' method of the font driver, we +should invoke the 'shape' method. 'hbfont_shape' should be extended +to pass to 'hb_shape_full' the required array of features, as +mentioned in the above HarfBuzz discussion. + ** Extend text-properties and overlays *** Several text-property planes This would get us rid of font-lock-face property (and I'd be happy to @@ -529,10 +577,6 @@ from the emacsclient process. ** Optionally make the cursor a little thinner at the end of a line or the end of the buffer. -** Port the conservative stack marking code of Emacs's garbage collector - to more systems, so that we can completely get rid of GCPROs. Note - that Boehm garbage collector provides this. - ** Reorder defcustom's in each package so that the more important options come first in the Customize buffers. This could be done by either rearranging the file (since options are shown in the order @@ -1469,17 +1513,25 @@ presence of multi-file documents. ** Replace linum.el with nlinum.el https://lists.gnu.org/r/emacs-devel/2013-08/msg00379.html + (Since Emacs 26 introduced native line numbers, this item is + probably obsolete.) + ** Merge sendmail.el and messages.el. Probably not a complete merge, but at least arrange for messages.el to be a derived mode of sendmail.el. Or arrange for messages.el to be split into a small core and "the rest" so that we use less resources as long as we stick to the features provided in sendmail.el. + (Probably obsolete, as Emacs 24 switched to message.el as the + default mail composer.) + ** Replace gmalloc.c with the modified Doug Lea code from the current GNU libc so that the special mmapping of buffers can be removed -- that apparently loses under Solaris, at least. [fx has mostly done this.] + (Obsolete, since gmalloc.c is nowadays only used on MS-DOS.) + ** Rewrite make-docfile to be clean and maintainable. It might be better to replace it with Lisp, using the byte compiler. https://lists.gnu.org/r/emacs-devel/2012-06/msg00037.html