When you add a new item, use the appropriate mark if you are sure it applies,
otherwise leave it unmarked.
+\f
+* Installation Changes in Emacs 25.2
+
+** The new option 'configure --enable-gcc-warnings=warn-only' causes
+GCC to issue warnings without stopping the build. This behavior is
+now the default in developer builds. As before, use
+'--disable-gcc-warnings' to suppress GCC's warnings, and
+'--enable-gcc-warnings' to stop the build if GCC issues warnings.
+
++++
+** The Emacs server now has socket-launching support. This allows
+socket based activation, where an external process like systemd can
+invoke the Emacs server process upon a socket connection event and
+hand the socket over to Emacs. Emacs uses this socket to service
+emacsclient commands. This new functionality can be disabled with the
+configure option '--disable-libsystemd'.
+
+** New configure option '--disable-build-details' attempts to build an
+Emacs that is more likely to be reproducible; that is, if you build
+and install Emacs twice, the second Emacs is a copy of the first.
+Deterministic builds omit the build date from the output of the
+emacs-version and erc-cmd-SV functions, and the leave the following
+variables nil: emacs-build-system, emacs-build-time,
+erc-emacs-build-time.
+
+** Emacs no longer works on IRIX. We expect that Emacs users are not
+affected by this, as SGI stopped supporting IRIX in December 2013.
+
+\f
+* Startup Changes in Emacs 25.2
+
+\f
+* Changes in Emacs 25.2
+
+---
+** 'find-library-name' will now fall back on looking at 'load-history'
+to try to locate libraries that have been loaded with an explicit path
+outside 'load-path'.
+
++++
+** Faces in 'minibuffer-prompt-properties' no longer overwrite properties
+in the text in functions like 'read-from-minibuffer', but instead are
+added to the end of the face list. This allows users to say things
+like '(read-from-minibuffer (propertize "Enter something: " 'face 'bold))'.
+
++++
+** The new variable 'extended-command-suggest-shorter' has been added
+to control whether to suggest shorter 'M-x' commands or not.
+
+---
+** icomplete now respects 'completion-ignored-extensions'.
+
++++
+** Non-breaking hyphens are now displayed with the 'nobreak-hyphen'
+face instead of the 'escape-glyph' face.
+
+---
+** 'C-x h' ('mark-whole-buffer') will now avoid marking the prompt
+part of minibuffers.
+
+---
+** 'find-library' now takes a prefix argument to pop to a different
+window.
+
+** 'find-library', 'help-function-def' and 'help-variable-def' now run
+'find-function-after-hook'.
+
+---
+** 'process-attributes' on Darwin systems now returns more information.
+
++++
+** Several accessors for the value returned by 'file-attributes'
+have been added. They are: 'file-attribute-type',
+'file-attribute-link-number', 'file-attribute-user-id',
+'file-attribute-group-id', 'file-attribute-access-time',
+'file-attribute-modification-time',
+'file-attribute-status-change-time', 'file-attribute-size',
+'file-attribute-modes', 'file-attribute-inode-number', and
+'file-attribute-device-number'.
+
++++
+** The new function 'buffer-hash' computes a fast, non-consing hash of
+a buffer's contents.
+
+---
+** 'fill-paragraph' no longer marks the buffer as changed unless it
+actually changed something.
+
+---
+** The locale language name 'ca' is now mapped to the language
+environment 'Catalan', which has been added.
+
+---
+** 'align-regexp' has a separate history for its interactive argument.
+'align-regexp' no longer shares its history with all other
+history-less functions that use 'read-string'.
+
++++
+** The networking code has been reworked so that it's more
+asynchronous than it was (when specifying :nowait t in
+'make-network-process'). How asynchronous it is varies based on the
+capabilities of the system, but on a typical GNU/Linux system the DNS
+resolution, the connection, and (for TLS streams) the TLS negotiation
+are all done without blocking the main Emacs thread. To get
+asynchronous TLS, the TLS boot parameters have to be passed in (see
+the manual for details).
+
+Certain process oriented functions (like 'process-datagram-address')
+will block until socket setup has been performed. The recommended way
+to deal with asynchronous sockets is to avoid interacting with them
+until they have changed status to "run". This is most easily done
+from a process sentinel.
+
+** 'make-network-process' and 'open-network-stream' sometimes allowed
+:service to be an integer string (e.g., :service "993") and sometimes
+required an integer (e.g., :service 993). This difference has been
+eliminated, and integer strings work everywhere.
+
+** It is possible to disable attempted recovery on fatal signals.
+
+Two new variables support disabling attempts to recover from stack
+overflow and to avoid automatic auto-save when Emacs is delivered a
+fatal signal. 'attempt-stack-overflow-recovery', if set to 'nil',
+will disable attempts to recover from C stack overflows; Emacs will
+then crash as with any other fatal signal.
+'attempt-orderly-shutdown-on-fatal-signal', if set to 'nil', will
+disable attempts to auto-save the session and shut down in an orderly
+fashion when Emacs receives a fatal signal; instead, Emacs will
+terminate immediately. Both variables are non-'nil' by default.
+These variables are for users who would like to avoid the small
+probability of data corruption due to techniques Emacs uses to recover
+in these situations.
+
++++
+** File local and directory local variables are now initialized each
+time the major mode is set, not just when the file is first visited.
+These local variables will thus not vanish on setting a major mode.
+
++++
+** A second dir-local file (.dir-locals-2.el) is now accepted.
+See the variable 'dir-locals-file-2' for more information.
+
+---
+** International domain names (IDNA) are now encoded via the new
+puny.el library, so that one can visit web sites with non-ASCII URLs.
+
++++
+** The new 'timer-list' command lists all active timers in a buffer,
+where you can cancel them with the 'c' command.
+
++++
+** The new function 'read-multiple-choice' prompts for multiple-choice
+questions, with a handy way to display help texts.
+
++++
+** 'switch-to-buffer-preserve-window-point' now defaults to t.
+
+\f
+* Editing Changes in Emacs 25.2
+
++++
+** New bindings for 'query-replace-map'.
+'undo', undo the last replacement; bound to 'u'.
+'undo-all', undo all replacements; bound to 'U'.
+
+\f
+* Changes in Specialized Modes and Packages in Emacs 25.2
+
+** Compilation mode
+
+---
+*** Messages from CMake are now recognized.
+
+** Dired
+
++++
+*** In wdired, when editing files to contain slash characters,
+the resulting directories are automatically created. Whether to do
+this is controlled by the 'wdired-create-parent-directories' variable.
+
++++
+*** 'W' is now bound to 'browse-url-of-dired-file', and is useful for
+viewing HTML files and the like.
+
+** Ediff
+
+*** Ediff can be prevented from pausing 1 second after reaching a
+breakpoint (e.g. with "f" and "o") by customizing the new option
+'edebug-sit-on-break'.
+
+** eww
+
++++
+*** A new 's' command for switching to another eww buffer via the minibuffer.
+
+---
+*** The 'o' command ('shr-save-contents') has moved to 'O' to avoid collision
+with the 'o' command from 'image-map'.
+
++++
+*** A new command 'C' ('eww-toggle-colors') can be used to toggle
+whether to use the HTML-specified colors or not. The user can also
+customize the 'shr-use-colors' variable.
+
+---
+*** Images that are being loaded are now marked with gray
+"placeholder" images of the size specified by the HTML. They are then
+replaced by the real images asynchronously, which will also now
+respect width/height HTML specs (unless they specify widths/heights
+bigger than the current window).
+
+** Images
+
++++
+*** Images are automatically scaled before displaying based on the
+'image-scaling-factor' variable (if Emacs supports scaling the images
+in question).
+
++++
+*** Images inserted with 'insert-image' and related functions get a
+keymap put into the text properties (or overlays) that span the
+image. This keymap binds keystrokes for manipulating size and
+rotation, as well as saving the image to a file. These commands are
+also available in 'image-mode'.
+
++++
+*** A new library for creating and manipulating SVG images has been
+added. See the "SVG Images" section in the lispref manual for
+details.
+
++++
+*** New setf-able function to access and set image parameters is
+provided: 'image-property'.
+
+---
+** The default 'Info-default-directory-list' no longer checks some obsolete
+directory suffixes (gnu, gnu/lib, gnu/lib/emacs, emacs, lib, lib/emacs)
+when searching for info directories.
+
++++
+** The commands that add ChangeLog entries now prefer a VCS root directory
+for the ChangeLog file, if none already exists. Customize
+'change-log-directory-files' to nil for the old behavior.
+
+---
+** Support for non-string values of 'time-stamp-format' has been removed.
+
+** Message
+
+---
+*** 'message-use-idna' now defaults to t (because Emacs comes with
+built-in IDNA support now).
+
+---
+*** When sending HTML messages with embedded images, and you have
+exiftool installed, and you rotate images with EXIF data (i.e.,
+JPEGs), the rotational information will be inserted into the outgoing
+image in the message. (The original image will not have its
+orientation affected.)
+
+---
+*** The 'message-valid-fqdn-regexp' variable has been removed, since
+there are now top-level domains added all the time. Message will no
+longer warn about sending emails to top-level domains it hasn't heard
+about.
+
+*** 'message-beginning-of-line' (bound to C-a) understands folded headers.
+In 'visual-line-mode' it will look for the true beginning of a header
+while in non-'visual-line-mode' it will move the point to the indented
+header's value.
+
+** Tramp
+
++++
+*** New connection method "sg", which supports editing files under a
+different group ID.
+
++++
+*** New connection method "doas" for OpenBSD hosts.
+
+---
+** 'auto-revert-use-notify' is set back to t in 'global-auto-revert-mode'.
+
+** CSS mode
+
+---
+*** Support for completing attribute values, at-rules, bang-rules, and
+HTML tags using the 'completion-at-point' command.
+
++++
+** Emacs now supports character name escape sequences in character and
+string literals. The syntax variants \N{character name} and
+\N{U+code} are supported.
+
++++
+** Prog mode has some support for multi-mode indentation.
+This allows better indentation support in modes that support multiple
+programming languages in the same buffer, like literate programming
+environments or ANTLR programs with embedded Python code.
+
+A major mode can provide indentation context for a sub-mode through
+the 'prog-indentation-context' variable. To support this, modes that
+provide indentation should use 'prog-widen' instead of 'widen' and
+'prog-first-column' instead of a literal zero. See the node
+"Mode-Specific Indent" in the ELisp manual for more details.
+
+** ERC
+
+*** New variable 'erc-default-port-tls' used to connect to TLS IRC
+servers.
+
+** URL
+
++++
+*** The new function 'url-cookie-delete-cookie' can be used to
+programmatically delete all cookies, or cookies from a specific
+domain.
+
++++
+*** 'url-retrieve-synchronously' now takes an optional timeout parameter.
+
+---
+*** The URL package now support HTTPS over proxies supporting CONNECT.
+
++++
+*** 'url-user-agent' now defaults to 'default', and the User-Agent
+string is computed dynamically based on 'url-privacy-level'.
+
+** VC and related modes
+
+---
+*** The VC state indicator in the mode line now defaults to more
+colorful faces to make it more obvious to the user what the state is.
+See the 'vc-faces' customization group.
+
+** CC mode
+
+*** Opening a .h file will turn C or C++ mode depending on language used.
+This is done with the help of 'c-or-c++-mode' function which analyses
+contents of the buffer to determine whether it's a C or C++ source
+file.
+
+\f
+* New Modes and Packages in Emacs 25.2
+
+** New Elisp data-structure library `radix-tree'.
+
+\f
+* Incompatible Lisp Changes in Emacs 25.2
+
++++
+** Resizing a frame no longer runs 'window-configuration-change-hook'.
+Put your function on 'window-size-change-functions' instead.
+
+** 'C-up', 'C-down', 'C-left' and 'C-right' are now defined in term
+mode to send the same escape sequences that xterm does. This makes
+things like forward-word in readline work.
+
+---
+** hideshow mode got four key bindings that are analogous to outline
+mode bindings: 'C-c @ C-a', 'C-c @ C-t', 'C-c @ C-d', and 'C-c @ C-e.'
+
+** The grep/rgrep/lgrep functions will now ask about saving files
+before running. This is controlled by the 'grep-save-buffers'
+variable.
+
++++
+** The variable 'text-quoting-style' no longer affects the treatment
+of curved quotes in format arguments to functions like 'message' and
+'format-message'. In particular, when this variable's value is
+'grave', all quotes in formats are output as-is.
+
+** Functions like 'check-declare-file' and 'check-declare-directory'
+now generate less chatter and more-compact diagnostics. The auxiliary
+function 'check-declare-errmsg' has been removed.
+
+\f
+* Lisp Changes in Emacs 25.2
+
+** New var `definition-prefixes' is a hashtable mapping prefixes to the
+files where corresponding definitions can be found. This can be used
+to fetch definitions that are not yet loaded, for example for `C-h f'.
+
+** New var syntax-ppss-table to control the syntax-table used in syntax-ppss.
+
++++
+** 'define-derived-mode' can now specify an :after-hook form, which
+gets evaluated after the new mode's hook has run. This can be used to
+incorporate configuration changes made in the mode hook into the
+mode's setup.
+
+** Autoload files can be generated without timestamps,
+by setting 'autoload-timestamps' to nil.
+FIXME As an experiment, nil is the current default.
+If no insurmountable problems before next release, it can stay that way.
+
+---
+** 'gnutls-boot' now takes a parameter :complete-negotiation that says
+that negotiation should complete even on non-blocking sockets.
+
++++
+** New functions 'window-pixel-width-before-size-change' and
+'window-pixel-height-before-size-change' support detecting which
+window changed size when 'window-size-change-functions' are run.
+
++++
+** New function 'display-buffer-reuse-mode-window' is an action function
+suitable for use in 'display-buffer-alist'. For example, to avoid creating
+a new window when opening man pages when there's already one, use
+(add-to-list 'display-buffer-alist
+ '("\\`\\*Man .*\\*\\'" .
+ (display-buffer-reuse-mode-window
+ (inhibit-same-window . nil)
+ (mode . Man-mode))))
+
+---
+** There is now a new variable 'flyspell-sort-corrections-function'
+that allows changing the way corrections are sorted.
+
+---
+** The new command 'fortune-message' has been added, which displays
+fortunes in the echo area.
+
++++
+** New function 'func-arity' returns information about the argument list
+of an arbitrary function. This generalizes 'subr-arity' for functions
+that are not built-in primitives. We recommend using this new
+function instead of 'subr-arity'.
+
++++
+** 'parse-partial-sexp' state has a new element. Element 10 is
+non-nil when the last character scanned might be the first character
+of a two character construct, i.e., a comment delimiter or escaped
+character. Its value is the syntax of that last character.
+
++++
+** 'parse-partial-sexp''s state, element 9, has now been confirmed as
+permanent and documented, and may be used by Lisp programs. Its value
+is a list of currently open parenthesis positions, starting with the
+outermost parenthesis.
+
+---
+** 'read-color' will now display the color names using the color itself
+as the background color.
+
+** The function 'redirect-debugging-output' now works on platforms
+other than GNU/Linux.
+
++++
+** The new function 'string-version-lessp' compares strings by
+interpreting consecutive runs of numerical characters as numbers, and
+compares their numerical values. According to this predicate,
+"foo2.png" is smaller than "foo12.png".
+
++++
+** The new function 'char-from-name' converts a Unicode name string
+to the corresponding character code.
+
++++
+** New functions 'sxhash-eq' and 'sxhash-eql' return hash codes of a
+Lisp object suitable for use with 'eq' and 'eql' correspondingly. If
+two objects are 'eq' ('eql'), then the result of 'sxhash-eq'
+('sxhash-eql') on them will be the same.
+
++++
+** Function 'sxhash' has been renamed to 'sxhash-equal' for
+consistency with the new functions. For compatibility, 'sxhash'
+remains as an alias to 'sxhash-equal'.
+
++++
+** Time conversion functions that accept a time zone rule argument now
+allow it to be OFFSET or a list (OFFSET ABBR), where the integer
+OFFSET is a count of seconds east of Universal Time, and the string
+ABBR is a time zone abbreviation. The affected functions are
+'current-time-string', 'current-time-zone', 'decode-time',
+'format-time-string', and 'set-time-zone-rule'.
+
++++
+*** New basic face 'fixed-pitch-serif', for a fixed-width font with serifs.
+The Info-quoted and tex-verbatim faces now default to inheriting from it.
+
+\f
+* Changes in Emacs 25.2 on Non-Free Operating Systems
+
+** Intercepting hotkeys on Windows 7 and later now works better.
+The new keyboard hooking code properly grabs system hotkeys such as
+Win-* and Alt-TAB, in a way that Emacs can get at them before the
+system. This makes the 'w32-register-hot-key' functionality work
+again on all versions of MS-Windows starting with Windows 7. On
+Windows NT and later you can now register any hotkey combination. (On
+Windows 9X, the previous limitations, spelled out in the Emacs manual,
+still apply.)
+
+** 'convert-standard-filename' no longer mirrors slashes on MS-Windows.
+Previously, on MS-Windows this function converted slash characters in
+file names into backslashes. It no longer does that.
+
\f
* Installation Changes in Emacs 25.1
MS-Windows doesn't support UTF-8 as codeset in its locales.
+++
- ** New function 'alist-get', which is also a valid place (aka lvalue).
+ ** New function 'alist-get', which is a generalized variable
+ suitable for use with 'setf'.
+++
** New function 'funcall-interactively', which works like 'funcall'
"The Common Lisp `loop' macro.
Valid clauses include:
For clauses:
- for VAR from/upfrom/downfrom EXPR1 to/upto/downto/above/below EXPR2 by EXPR3
+ for VAR from/upfrom/downfrom EXPR1 to/upto/downto/above/below EXPR2 [by EXPR3]
for VAR = EXPR1 then EXPR2
- for VAR in/on/in-ref LIST by FUNC
+ for VAR in/on/in-ref LIST [by FUNC]
for VAR across/across-ref ARRAY
for VAR being:
the elements of/of-ref SEQUENCE [using (index VAR2)]
(let ((accessor (intern (format "%s%s" conc-name slot))))
(push slot slots)
(push (nth 1 desc) defaults)
+ ;; The arg "cl-x" is referenced by name in eg pred-form
+ ;; and pred-check, so changing it is not straightforward.
(push `(cl-defsubst ,accessor (cl-x)
+ ,(format "Access slot \"%s\" of `%s' struct CL-X."
+ slot struct)
(declare (side-effect-free t))
,@(and pred-check
(list `(or ,pred-check
(require 'cl-lib)
(require 'help-mode)
+(require 'radix-tree)
(defvar help-fns-describe-function-functions nil
"List of functions to run in help buffer in `describe-function'.
;; Functions
+(defvar help-definition-prefixes nil
+ ;; FIXME: We keep `definition-prefixes' as a hash-table so as to
+ ;; avoid pre-loading radix-tree and because it takes slightly less
+ ;; memory. But when we use this table it's more efficient to
+ ;; represent it as a radix tree, since the main operation is to do
+ ;; `radix-tree-prefixes'. Maybe we should just bite the bullet and
+ ;; use a radix tree for `definition-prefixes' (it's not *that*
+ ;; costly, really).
+ "Radix-tree representation replacing `definition-prefixes'.")
+
+(defun help-definition-prefixes ()
+ "Return the up-to-date radix-tree form of `definition-prefixes'."
+ (when (> (hash-table-count definition-prefixes) 0)
+ (maphash (lambda (prefix files)
+ (let ((old (radix-tree-lookup help-definition-prefixes prefix)))
+ (setq help-definition-prefixes
+ (radix-tree-insert help-definition-prefixes
+ prefix (append old files)))))
+ definition-prefixes)
+ (clrhash definition-prefixes))
+ help-definition-prefixes)
+
+(defun help--loaded-p (file)
+ "Try and figure out if FILE has already been loaded."
+ (or (let ((feature (intern-soft file)))
+ (and feature (featurep feature)))
+ (let* ((re (load-history-regexp file))
+ (done nil))
+ (dolist (x load-history)
+ (if (string-match-p re (car x)) (setq done t)))
+ done)))
+
+(defun help--load-prefixes (prefixes)
+ (pcase-dolist (`(,prefix . ,files) prefixes)
+ (setq help-definition-prefixes
+ (radix-tree-insert help-definition-prefixes prefix nil))
+ (dolist (file files)
+ ;; FIXME: Should we scan help-definition-prefixes to remove
+ ;; other prefixes of the same file?
+ ;; FIXME: this regexp business is not good enough: for file
+ ;; `toto', it will say `toto' is loaded when in reality it was
+ ;; just cedet/semantic/toto that has been loaded.
+ (unless (help--loaded-p file)
+ (load file 'noerror 'nomessage)))))
+
+(defun help--symbol-completion-table (string pred action)
+ (let ((prefixes (radix-tree-prefixes (help-definition-prefixes) string)))
+ (help--load-prefixes prefixes))
+ (let ((prefix-completions
+ (mapcar #'intern (all-completions string definition-prefixes))))
+ (complete-with-action action obarray string
+ (if pred (lambda (sym)
+ (or (funcall pred sym)
+ (memq sym prefix-completions)))))))
+
(defvar describe-function-orig-buffer nil
"Buffer that was current when `describe-function' was invoked.
Functions on `help-fns-describe-function-functions' can use this
(setq val (completing-read (if fn
(format "Describe function (default %s): " fn)
"Describe function: ")
- obarray 'fboundp t nil nil
- (and fn (symbol-name fn))))
+ #'help--symbol-completion-table
+ #'fboundp
+ t nil nil (and fn (symbol-name fn))))
(list (if (equal val "")
fn (intern val)))))
(or (and function (symbolp function))
(sig-key (if (subrp def)
(indirect-function real-def)
real-def))
- (file-name (find-lisp-object-file-name function def))
+ (file-name (find-lisp-object-file-name function (if aliased 'defun
+ def)))
(pt1 (with-current-buffer (help-buffer) (point)))
(beg (if (and (or (byte-code-function-p def)
(keymapp def)
;; Print what kind of function-like object FUNCTION is.
(princ (cond ((or (stringp def) (vectorp def))
"a keyboard macro")
- ((subrp def)
- (if (eq 'unevalled (cdr (subr-arity def)))
- (concat beg "special form")
- (concat beg "built-in function")))
;; Aliases are Lisp functions, so we need to check
;; aliases before functions.
(aliased
(format-message "an alias for `%s'" real-def))
+ ((subrp def)
+ (if (eq 'unevalled (cdr (subr-arity def)))
+ (concat beg "special form")
+ (concat beg "built-in function")))
((autoloadp def)
(format "%s autoloaded %s"
(if (commandp def) "an interactive" "an")
(interactive
(let ((v (variable-at-point))
(enable-recursive-minibuffers t)
+ (orig-buffer (current-buffer))
val)
- (setq val (completing-read (if (symbolp v)
- (format
- "Describe variable (default %s): " v)
- "Describe variable: ")
- obarray
- (lambda (vv)
- (or (get vv 'variable-documentation)
- (and (boundp vv) (not (keywordp vv)))))
- t nil nil
- (if (symbolp v) (symbol-name v))))
+ (setq val (completing-read
+ (if (symbolp v)
+ (format
+ "Describe variable (default %s): " v)
+ "Describe variable: ")
+ #'help--symbol-completion-table
+ (lambda (vv)
+ ;; In case the variable only exists in the buffer
+ ;; the command we switch back to that buffer before
+ ;; we examine the variable.
+ (with-current-buffer orig-buffer
+ (or (get vv 'variable-documentation)
+ (and (boundp vv) (not (keywordp vv))))))
+ t nil nil
+ (if (symbolp v) (symbol-name v))))
(list (if (equal val "")
v (intern val)))))
(let (file-name)
(unless valvoid
(with-current-buffer standard-output
(setq val-start-pos (point))
- (princ "value is ")
- (let ((from (point))
- (line-beg (line-beginning-position))
+ (princ "value is")
+ (let ((line-beg (line-beginning-position))
(print-rep
(let ((rep
(let ((print-quoted t))
(format-message "`%s'" rep)
rep))))
(if (< (+ (length print-rep) (point) (- line-beg)) 68)
- (insert print-rep)
+ (insert " " print-rep)
(terpri)
(pp val)
- (if (< (point) (+ 68 (line-beginning-position 0)))
- (delete-region from (1+ from))
- (delete-region (1- from) from)))
+ ;; Remove trailing newline.
+ (delete-char -1))
(let* ((sv (get variable 'standard-value))
(origval (and (consp sv)
(condition-case nil
(eval (car sv))
- (error :help-eval-error)))))
+ (error :help-eval-error))))
+ from)
(when (and (consp sv)
(not (equal origval val))
(not (equal origval :help-eval-error)))
(buffer-file-name buffer)))
(dir-locals-find-file
(buffer-file-name buffer))))
- (dir-file t))
+ (is-directory nil))
(princ (substitute-command-keys
" This variable's value is directory-local"))
- (if (null file)
- (princ ".\n")
- (princ ", set ")
- (if (consp file) ; result from cache
- ;; If the cache element has an mtime, we
- ;; assume it came from a file.
- (if (nth 2 file)
- (setq file (expand-file-name
- dir-locals-file (car file)))
- ;; Otherwise, assume it was set directly.
- (setq file (car file)
- dir-file nil)))
- (princ (substitute-command-keys
- (if dir-file
- "by the file\n `"
- "for the directory\n `")))
+ (when (consp file) ; result from cache
+ ;; If the cache element has an mtime, we
+ ;; assume it came from a file.
+ (if (nth 2 file)
+ ;; (car file) is a directory.
+ (setq file (dir-locals--all-files (car file)))
+ ;; Otherwise, assume it was set directly.
+ (setq file (car file)
+ is-directory t)))
+ (if (null file)
+ (princ ".\n")
+ (princ ", set ")
+ (princ (substitute-command-keys
+ (cond
+ (is-directory "for the directory\n `")
+ ;; Many files matched.
+ ((and (consp file) (cdr file))
+ (setq file (file-name-directory (car file)))
+ (format "by one of the\n %s files in the directory\n `"
+ dir-locals-file))
+ (t (setq file (car file))
+ "by the file\n `"))))
(with-current-buffer standard-output
(insert-text-button
file 'type 'help-dir-local-var-def
- 'help-args (list variable file)))
+ 'help-args (list variable file)))
(princ (substitute-command-keys "'.\n"))))
(princ (substitute-command-keys
" This variable's value is file-local.\n"))))
(if (or (not (vectorp docs)) (/= (length docs) 95))
(error "Invalid first extra slot in this category table\n"))
(with-current-buffer standard-output
- (insert "Legend of category mnemonics (see the tail for the longer description)\n")
+ (setq-default help-button-cache (make-marker))
+ (insert "Legend of category mnemonics ")
+ (insert-button "(longer descriptions at the bottom)"
+ 'action help-button-cache
+ 'follow-link t
+ 'help-echo "mouse-2, RET: show full legend")
+ (insert "\n")
(let ((pos (point)) (items 0) lines n)
(dotimes (i 95)
(if (aref docs i) (setq items (1+ items))))
"character(s)\tcategory mnemonics\n"
"------------\t------------------")
(describe-vector table 'help-describe-category-set)
+ (set-marker help-button-cache (point))
(insert "Legend of category mnemonics:\n")
(dotimes (i 95)
(let ((elt (aref docs i)))
(when (and mouse-1-click-follows-link
(eq (if (eq mouse-1-click-follows-link 'double)
'double-down-mouse-1 'down-mouse-1)
- (car-safe last-input-event))
- (mouse-on-link-p (event-start last-input-event))
- (or mouse-1-click-in-non-selected-windows
- (eq (selected-window)
- (posn-window (event-start last-input-event)))))
- (let ((timedout
- (sit-for (if (numberp mouse-1-click-follows-link)
- (/ (abs mouse-1-click-follows-link) 1000.0)
- 0))))
- (if (if (and (numberp mouse-1-click-follows-link)
- (>= mouse-1-click-follows-link 0))
- timedout (not timedout))
- nil
-
- (let ((event (read-key))) ;Use read-key so it works for xterm-mouse-mode!
- (if (eq (car-safe event) (if (eq mouse-1-click-follows-link 'double)
- 'double-mouse-1 'mouse-1))
- ;; Turn the mouse-1 into a mouse-2 to follow links.
- (let ((newup (if (eq mouse-1-click-follows-link 'double)
- 'double-mouse-2 'mouse-2)))
- ;; If mouse-2 has never been done by the user, it doesn't have
- ;; the necessary property to be interpreted correctly.
- (unless (get newup 'event-kind)
- (put newup 'event-kind (get (car event) 'event-kind)))
- (push (cons newup (cdr event)) unread-command-events)
- ;; Don't change the down event, only the up-event (bug#18212).
- nil)
- (push event unread-command-events)
- nil))))))
+ (car-safe last-input-event)))
+ (let ((action (mouse-on-link-p (event-start last-input-event))))
+ (when (and action
+ (or mouse-1-click-in-non-selected-windows
+ (eq (selected-window)
+ (posn-window (event-start last-input-event)))))
+ (let ((timedout
+ (sit-for (if (numberp mouse-1-click-follows-link)
+ (/ (abs mouse-1-click-follows-link) 1000.0)
+ 0))))
+ (if (if (and (numberp mouse-1-click-follows-link)
+ (>= mouse-1-click-follows-link 0))
+ timedout (not timedout))
+ nil
+ ;; Use read-key so it works for xterm-mouse-mode!
+ (let ((event (read-key)))
+ (if (eq (car-safe event)
+ (if (eq mouse-1-click-follows-link 'double)
+ 'double-mouse-1 'mouse-1))
+ (progn
+ ;; Turn the mouse-1 into a mouse-2 to follow links,
+ ;; but only if ‘mouse-on-link-p’ hasn’t returned a
+ ;; string or vector (see its docstring).
+ (if (or (stringp action) (vectorp action))
+ (push (aref action 0) unread-command-events)
+ (let ((newup (if (eq mouse-1-click-follows-link 'double)
+ 'double-mouse-2 'mouse-2)))
+ ;; If mouse-2 has never been done by the user, it
+ ;; doesn't have the necessary property to be
+ ;; interpreted correctly.
+ (unless (get newup 'event-kind)
+ (put newup 'event-kind (get (car event) 'event-kind)))
+ (push (cons newup (cdr event)) unread-command-events)))
+ ;; Don't change the down event, only the up-event
+ ;; (bug#18212).
+ nil)
+ (push event unread-command-events)
+ nil))))))))
(define-key key-translation-map [down-mouse-1]
#'mouse--down-1-maybe-follows-link)
(if (fboundp mm-fun) ; bug#20201
`(keymap
,indicator
- (turn-off menu-item "Turn Off minor mode" ,mm-fun)
+ (turn-off menu-item "Turn off minor mode" ,mm-fun)
(help menu-item "Help for minor mode"
(lambda () (interactive)
(describe-function ',mm-fun)))))))
(mouse-minibuffer-check click)
(select-window (posn-window (event-start click)))
(let ((beg (posn-point (event-start click)))
- (end (posn-point (event-end click)))
+ (end
+ (if (eq (posn-window (event-end click)) (selected-window))
+ (posn-point (event-end click))
+ ;; If the mouse ends up in any other window or on the menu
+ ;; bar, use `window-point' of selected window (Bug#23707).
+ (window-point)))
(click-count (event-click-count click)))
(let ((drag-start (terminal-parameter nil 'mouse-drag-start)))
(when drag-start
(declare-function orgtbl-mode "org-table" (&optional arg))
(declare-function org-clock-out "org-clock" (&optional switch-to-state fail-quietly at-time))
-(declare-function org-beamer-mode "ox-beamer" ())
+(declare-function org-beamer-mode "ox-beamer" (&optional prefix) t)
(declare-function org-table-edit-field "org-table" (arg))
(declare-function org-table-justify-field-maybe "org-table" (&optional new))
(declare-function org-table-set-constants "org-table" ())
(declare-function org-id-get-create "org-id" (&optional force))
(declare-function org-id-find-id-file "org-id" (id))
(declare-function org-tags-view "org-agenda" (&optional todo-only match))
-(declare-function org-agenda-list "org-agenda" (&optional arg start-day span))
+(declare-function org-agenda-list "org-agenda"
+ (&optional arg start-day span with-hour))
(declare-function org-agenda-redo "org-agenda" (&optional all))
(declare-function org-table-align "org-table" ())
(declare-function org-table-begin "org-table" (&optional table-type))
(declare-function org-element-interpret-data "org-element"
(data &optional parent))
(declare-function org-element-map "org-element"
- (data types fun &optional info first-match no-recursion))
+ (data types fun &optional
+ info first-match no-recursion with-affiliated))
(declare-function org-element-nested-p "org-element" (elem-a elem-b))
(declare-function org-element-parse-buffer "org-element"
(&optional granularity visible-only))
(defvar org-export--registered-backends) ; From ox.el.
(declare-function org-export-derived-backend-p "ox" (backend &rest backends))
-(declare-function org-export-backend-name "ox" (backend))
+(declare-function org-export-backend-name "ox" (backend) t)
+(declare-function org-export-backend-options "ox" (cl-x) t)
(defcustom org-export-backends '(ascii html icalendar latex)
"List of export back-ends that should be always available.
(defvar mark-active)
;; Various packages
-(declare-function calendar-absolute-from-iso "cal-iso" (date))
+(declare-function calendar-iso-to-absolute "cal-iso" (date))
(declare-function calendar-forward-day "cal-move" (arg))
(declare-function calendar-goto-date "cal-move" (date))
(declare-function calendar-goto-today "cal-move" ())
(declare-function dired-get-filename "dired" (&optional localp no-error-if-not-filep))
(defvar font-lock-unfontify-region-function)
(declare-function iswitchb-read-buffer "iswitchb"
- (prompt &optional default require-match start matches-set))
+ (prompt &optional
+ default require-match _predicate start matches-set))
(defvar iswitchb-temp-buflist)
(declare-function org-gnus-follow-link "org-gnus" (&optional group article))
(defvar org-agenda-tags-todo-honor-ignore-options)
(declare-function org-agenda-skip "org-agenda" ())
(declare-function
org-agenda-format-item "org-agenda"
- (extra txt &optional level category tags dotime noprefix remove-re habitp))
+ (extra txt &optional level category tags dotime remove-re habitp))
(declare-function org-agenda-new-marker "org-agenda" (&optional pos))
(declare-function org-agenda-change-all-lines "org-agenda"
(newhead hdmarker &optional fixface just-this))
;; Other stuff we need.
(require 'time-date)
-(unless (fboundp 'time-subtract) (defalias 'time-subtract 'subtract-time))
(require 'easymenu)
(require 'overlay)
(apply 'encode-time
(append (list 0 (* r (floor (+ .5 (/ (float (nth 1 time)) r)))))
(nthcdr 2 time))))
- (if (and past (< (org-float-time (time-subtract (current-time) res)) 0))
- (seconds-to-time (- (org-float-time res) (* r 60)))
+ (if (and past (< (float-time (time-subtract (current-time) res)) 0))
+ (seconds-to-time (- (float-time res) (* r 60)))
res))))
(defun org-today ()
(if (or (re-search-forward org-ts-regexp end t)
(re-search-forward org-ts-regexp-both end t))
(org-time-string-to-seconds (match-string 0))
- (org-float-time now))))
+ (float-time now))))
((= dcst ?c)
(let ((end (save-excursion (outline-next-heading) (point))))
(if (re-search-forward
(concat "^[ \t]*\\[" org-ts-regexp1 "\\]")
end t)
(org-time-string-to-seconds (match-string 0))
- (org-float-time now))))
+ (float-time now))))
((= dcst ?s)
(let ((end (save-excursion (outline-next-heading) (point))))
(if (re-search-forward org-scheduled-time-regexp end t)
(org-time-string-to-seconds (match-string 1))
- (org-float-time now))))
+ (float-time now))))
((= dcst ?d)
(let ((end (save-excursion (outline-next-heading) (point))))
(if (re-search-forward org-deadline-time-regexp end t)
(org-time-string-to-seconds (match-string 1))
- (org-float-time now))))
+ (float-time now))))
((= dcst ?p)
(if (re-search-forward org-priority-regexp (point-at-eol) t)
(string-to-char (match-string 2))
(lambda (x)
(if (or (string-match org-ts-regexp x)
(string-match org-ts-regexp-both x))
- (org-float-time
+ (float-time
(org-time-string-to-time (match-string 0 x)))
0))
comparefun (if (= dcst sorting-type) '< '>)))
;;;; Completion
-(declare-function org-export-backend-name "org-export" (cl-x))
-(declare-function org-export-backend-options "org-export" (cl-x))
(defun org-get-export-keywords ()
"Return a list of all currently understood export keywords.
Export keywords include options, block names, attributes and
(message "")))
(org-defkey map ">"
(lambda () (interactive)
- (org-eval-in-calendar '(scroll-calendar-left 1))))
+ (org-eval-in-calendar '(calendar-scroll-left 1))))
(org-defkey map "<"
(lambda () (interactive)
- (org-eval-in-calendar '(scroll-calendar-right 1))))
+ (org-eval-in-calendar '(calendar-scroll-right 1))))
(org-defkey map "\C-v"
(lambda () (interactive)
(org-eval-in-calendar
(defun org-time-stamp-to-now (timestamp-string &optional seconds)
"Difference between TIMESTAMP-STRING and now in days.
If SECONDS is non-nil, return the difference in seconds."
- (let ((fdiff (if seconds 'org-float-time 'time-to-days)))
+ (let ((fdiff (if seconds 'float-time 'time-to-days)))
(- (funcall fdiff (org-time-string-to-time timestamp-string))
(funcall fdiff (current-time)))))
(match-end (match-end 0))
(time1 (org-time-string-to-time ts1))
(time2 (org-time-string-to-time ts2))
- (t1 (org-float-time time1))
- (t2 (org-float-time time2))
+ (t1 (float-time time1))
+ (t2 (float-time time2))
(diff (abs (- t2 t1)))
(negative (< (- t2 t1) 0))
;; (ys (floor (* 365 24 60 60)))
(defun org-time-string-to-seconds (s)
"Convert a timestamp string to a number of seconds."
- (org-float-time (org-time-string-to-time s)))
+ (float-time (org-time-string-to-time s)))
(defun org-time-string-to-absolute (s &optional daynr prefer show-all buffer pos)
"Convert a time stamp to an absolute day number.
(+ (if (eq org-ts-what 'hour) n 0) (nth 2 time0))
(+ (if (eq org-ts-what 'day) n 0) (nth 3 time0))
(+ (if (eq org-ts-what 'month) n 0) (nth 4 time0))
- (+ (if (eq org-ts-what 'year) n 0) (nth 5 time0))
- (nthcdr 6 time0)))
+ (+ (if (eq org-ts-what 'year) n 0) (nth 5 time0))))
(when (and (member org-ts-what '(hour minute))
extra
(string-match "-\\([012][0-9]\\):\\([0-5][0-9]\\)" extra))
time-range, if possible.
The optional ZONE is omitted or nil for Emacs local time, t for
-Universal Time, `wall' for system wall clock time, or a string as in
-the TZ environment variable."
+Universal Time, `wall' for system wall clock time, or a string as
+in the TZ environment variable. It can also be a list (as from
+`current-time-zone') or an integer (as from `decode-time')
+applied without consideration for daylight saving time."
(format-time-string
format
(apply 'encode-time
;; Author: Fabián E. Gallina <fgallina@gnu.org>
;; URL: https://github.com/fgallina/python.el
-;; Version: 0.25.1
+;; Version: 0.25.2
;; Package-Requires: ((emacs "24.1") (cl-lib "1.0"))
;; Maintainer: emacs-devel@gnu.org
;; Created: Jul 2010
:version "24.3"
:link '(emacs-commentary-link "python"))
+
+;;; 24.x Compat
+\f
+
+(unless (fboundp 'prog-widen)
+ (defun prog-widen ()
+ (widen)))
+
+(unless (fboundp 'prog-first-column)
+ (defun prog-first-column ()
+ 0))
+
\f
;;; Bindings
;; Some util commands
(define-key map "\C-c\C-v" 'python-check)
(define-key map "\C-c\C-f" 'python-eldoc-at-point)
+ (define-key map "\C-c\C-d" 'python-describe-at-point)
;; Utilities
(substitute-key-definition 'complete-symbol 'completion-at-point
map global-map)
;; Builtin Exceptions
(,(rx symbol-start
(or
+ ;; Python 2 and 3:
"ArithmeticError" "AssertionError" "AttributeError" "BaseException"
- "DeprecationWarning" "EOFError" "EnvironmentError" "Exception"
- "FloatingPointError" "FutureWarning" "GeneratorExit" "IOError"
- "ImportError" "ImportWarning" "IndexError" "KeyError"
- "KeyboardInterrupt" "LookupError" "MemoryError" "NameError"
- "NotImplementedError" "OSError" "OverflowError"
- "PendingDeprecationWarning" "ReferenceError" "RuntimeError"
- "RuntimeWarning" "StopIteration" "SyntaxError" "SyntaxWarning"
- "SystemError" "SystemExit" "TypeError" "UnboundLocalError"
- "UnicodeDecodeError" "UnicodeEncodeError" "UnicodeError"
- "UnicodeTranslateError" "UnicodeWarning" "UserWarning" "VMSError"
- "ValueError" "Warning" "WindowsError" "ZeroDivisionError"
+ "BufferError" "BytesWarning" "DeprecationWarning" "EOFError"
+ "EnvironmentError" "Exception" "FloatingPointError" "FutureWarning"
+ "GeneratorExit" "IOError" "ImportError" "ImportWarning"
+ "IndentationError" "IndexError" "KeyError" "KeyboardInterrupt"
+ "LookupError" "MemoryError" "NameError" "NotImplementedError"
+ "OSError" "OverflowError" "PendingDeprecationWarning"
+ "ReferenceError" "RuntimeError" "RuntimeWarning" "StopIteration"
+ "SyntaxError" "SyntaxWarning" "SystemError" "SystemExit" "TabError"
+ "TypeError" "UnboundLocalError" "UnicodeDecodeError"
+ "UnicodeEncodeError" "UnicodeError" "UnicodeTranslateError"
+ "UnicodeWarning" "UserWarning" "ValueError" "Warning"
+ "ZeroDivisionError"
;; Python 2:
"StandardError"
;; Python 3:
- "BufferError" "BytesWarning" "IndentationError" "ResourceWarning"
- "TabError")
+ "BlockingIOError" "BrokenPipeError" "ChildProcessError"
+ "ConnectionAbortedError" "ConnectionError" "ConnectionRefusedError"
+ "ConnectionResetError" "FileExistsError" "FileNotFoundError"
+ "InterruptedError" "IsADirectoryError" "NotADirectoryError"
+ "PermissionError" "ProcessLookupError" "RecursionError"
+ "ResourceWarning" "StopAsyncIteration" "TimeoutError"
+ ;; OS specific
+ "VMSError" "WindowsError"
+ )
symbol-end) . font-lock-type-face)
;; Builtins
(,(rx symbol-start
(interactive)
(save-excursion
(save-restriction
- (widen)
+ (prog-widen)
(goto-char (point-min))
(let ((block-end))
(while (and (not block-end)
- Point is on a line starting a dedenter block.
- START is the position where the dedenter block starts."
(save-restriction
- (widen)
+ (prog-widen)
(let ((ppss (save-excursion
(beginning-of-line)
(syntax-ppss))))
happening for :at-dedenter-block-start context since the
possibilities can be narrowed to specific indentation points."
(save-restriction
- (widen)
+ (prog-widen)
(save-excursion
(pcase (python-indent-context)
- (`(:no-indent . ,_) 0)
+ (`(:no-indent . ,_) (prog-first-column)) ; usually 0
(`(,(or :after-line
:after-comment
:inside-string
(let ((opening-block-start-points
(python-info-dedenter-opening-block-positions)))
(if (not opening-block-start-points)
- 0 ; if not found default to first column
+ (prog-first-column) ; if not found default to first column
(mapcar (lambda (pos)
(save-excursion
(goto-char pos)
case INDENTATION is a list, this order is enforced."
(if (listp indentation)
(sort (copy-sequence indentation) #'<)
- (nconc (number-sequence 0 (1- indentation)
+ (nconc (number-sequence (prog-first-column) (1- indentation)
python-indent-offset)
(list indentation))))
(python-indent--previous-level levels (current-indentation))
(if levels
(apply #'max levels)
- 0))))
+ (prog-first-column)))))
(defun python-indent-line (&optional previous)
"Internal implementation of `python-indent-line-function'.
(defun python-shell-calculate-pythonpath ()
"Calculate the PYTHONPATH using `python-shell-extra-pythonpaths'."
(let ((pythonpath
- (tramp-compat-split-string
- (or (getenv "PYTHONPATH") "") path-separator)))
+ (split-string
+ (or (getenv "PYTHONPATH") "") path-separator 'omit)))
(python-shell--add-to-path-with-priority
pythonpath python-shell-extra-pythonpaths)
(mapconcat 'identity pythonpath path-separator)))
(md5 tramp-end-of-output)))
unset vars item)
(while env
- (setq item (tramp-compat-split-string (car env) "="))
+ (setq item (split-string (car env) "=" 'omit))
(setcdr item (mapconcat 'identity (cdr item) "="))
(if (and (stringp (cdr item)) (not (string-equal (cdr item) "")))
(push (format "%s %s" (car item) (cdr item)) vars)
\(Type \\[describe-mode] in the process buffer for a list of commands.)"
(when python-shell--parent-buffer
(python-util-clone-local-variables python-shell--parent-buffer))
+ (set (make-local-variable 'indent-tabs-mode) nil)
;; Users can interactively override default values for
;; `python-shell-interpreter' and `python-shell-interpreter-args'
;; when calling `run-python'. This ensures values let-bound in
(declare (indent 2))
(let* ((name (symbol-name name))
(function-name (intern (concat "python-skeleton--" name)))
- (msg (format-message
- "Add `%s' clause? " name)))
+ (msg (funcall (if (fboundp 'format-message) #'format-message #'format)
+ "Add `%s' clause? " name)))
(when (not skel)
(setq skel
`(< ,(format "%s:" name) \n \n
(unless (zerop (length docstring))
docstring)))))
+ (defvar-local python-eldoc-get-doc t
+ "Non-nil means eldoc should fetch the documentation
+ automatically. Set to nil by `python-eldoc-function' if
+ `python-eldoc-function-timeout-permanent' is non-nil and
+ `python-eldoc-function' times out.")
+
+ (defcustom python-eldoc-function-timeout 1
+ "Timeout for `python-eldoc-function' in seconds."
+ :group 'python
+ :type 'integer
+ :version "25.1")
+
+ (defcustom python-eldoc-function-timeout-permanent t
+ "Non-nil means that when `python-eldoc-function' times out
+ `python-eldoc-get-doc' will be set to nil"
+ :group 'python
+ :type 'boolean
+ :version "25.1")
+
(defun python-eldoc-function ()
"`eldoc-documentation-function' for Python.
For this to work as best as possible you should call
`python-shell-send-buffer' from time to time so context in
- inferior Python process is updated properly."
- (python-eldoc--get-doc-at-point))
+ inferior Python process is updated properly.
+
+ If `python-eldoc-function-timeout' seconds elapse before this
+ function returns then if
+ `python-eldoc-function-timeout-permanent' is non-nil
+ `python-eldoc-get-doc' will be set to nil and eldoc will no
+ longer return the documentation at the point automatically.
+
+ Set `python-eldoc-get-doc' to t to reenable eldoc documentation
+ fetching"
+ (when python-eldoc-get-doc
+ (with-timeout (python-eldoc-function-timeout
+ (if python-eldoc-function-timeout-permanent
+ (progn
+ (message "Eldoc echo-area display muted in this buffer, see `python-eldoc-function'")
+ (setq python-eldoc-get-doc nil))
+ (message "`python-eldoc-function' timed out, see `python-eldoc-function-timeout'")))
+ (python-eldoc--get-doc-at-point))))
(defun python-eldoc-at-point (symbol)
"Get help on SYMBOL using `help'.
nil nil symbol))))
(message (python-eldoc--get-doc-at-point symbol)))
+(defun python-describe-at-point (symbol process)
+ (interactive (list (python-info-current-symbol)
+ (python-shell-get-process)))
+ (comint-send-string process (concat "help('" symbol "')\n")))
+
\f
;;; Hideshow
This function can be used as the value of `add-log-current-defun-function'
since it returns nil if point is not inside a defun."
(save-restriction
- (widen)
+ (prog-widen)
(save-excursion
(end-of-line 1)
(let ((names)
(let ((point (python-info-dedenter-opening-block-position)))
(when point
(save-restriction
- (widen)
+ (prog-widen)
(message "Closes %s" (save-excursion
(goto-char point)
(buffer-substring
With optional argument LINE-NUMBER, check that line instead."
(save-excursion
(save-restriction
- (widen)
+ (prog-widen)
(when line-number
(python-util-goto-line line-number))
(while (and (not (eobp))
Optional argument LINE-NUMBER forces the line number to check against."
(save-excursion
(save-restriction
- (widen)
+ (prog-widen)
(when line-number
(python-util-goto-line line-number))
(when (python-info-line-ends-backslash-p)
where the continued line ends."
(save-excursion
(save-restriction
- (widen)
+ (prog-widen)
(let* ((context-type (progn
(back-to-indentation)
(python-syntax-context-type)))
(interactive)
(push-mark (point))
(push-mark (point-max) nil t)
- (goto-char (point-min)))
+ ;; This is really `point-min' in most cases, but if we're in the
+ ;; minibuffer, this is at the end of the prompt.
+ (goto-char (minibuffer-prompt-end)))
\f
;; Counting lines, one way or another.
(integer :tag "time" 2)
(other :tag "on")))
+(defcustom extended-command-suggest-shorter t
+ "If non-nil, show a shorter M-x invocation when there is one."
+ :group 'keyboard
+ :type 'boolean
+ :version "25.2")
+
(defun execute-extended-command--shorter-1 (name length)
(cond
((zerop length) (list ""))
((numberp suggest-key-bindings) suggest-key-bindings)
(t 2))))))
(when (and waited (not (consp unread-command-events)))
- (unless (or binding executing-kbd-macro (not (symbolp function))
+ (unless (or (not extended-command-suggest-shorter)
+ binding executing-kbd-macro (not (symbolp function))
(<= (length (symbol-name function)) 2))
;; There's no binding for CMD. Let's try and find the shortest
;; string to use in M-x.
This list is maintained by `undo-auto--undoable-change' and
`undo-auto--boundaries' and can be affected by changes to their
- default values.
-
- See also `undo-auto--buffer-undoably-changed'.")
+ default values.")
(defun undo-auto--add-boundary ()
"Add an `undo-boundary' in appropriate buffers."
(define-derived-mode process-menu-mode tabulated-list-mode "Process Menu"
"Major mode for listing the processes called by Emacs."
(setq tabulated-list-format [("Process" 15 t)
+ ("PID" 7 t)
("Status" 7 t)
("Buffer" 15 t)
("TTY" 12 t)
(defun process-menu-delete-process ()
"Kill process at point in a `list-processes' buffer."
(interactive)
- (delete-process (tabulated-list-get-id))
- (revert-buffer))
+ (let ((pos (point)))
+ (delete-process (tabulated-list-get-id))
+ (revert-buffer)
+ (goto-char (min pos (point-max)))
+ (if (eobp)
+ (forward-line -1)
+ (beginning-of-line))))
(defun list-processes--refresh ()
"Recompute the list of processes for the Process List buffer.
(process-query-on-exit-flag p))
(let* ((buf (process-buffer p))
(type (process-type p))
+ (pid (if (process-id p) (format "%d" (process-id p)) "--"))
(name (process-name p))
(status (symbol-name (process-status p)))
(buf-label (if (buffer-live-p buf)
(format " at %s b/s" speed)
"")))))
(mapconcat 'identity (process-command p) " "))))
- (push (list p (vector name status buf-label tty cmd))
+ (push (list p (vector name pid status buf-label tty cmd))
tabulated-list-entries))))))
(defun process-menu-visit-buffer (button)
This respects the wrapper hook `filter-buffer-substring-functions',
and the abnormal hook `buffer-substring-filters'.
No filtering is done unless a hook says to."
- (with-wrapper-hook filter-buffer-substring-functions (beg end delete)
+ (subr--with-wrapper-hook-no-warnings
+ filter-buffer-substring-functions (beg end delete)
(cond
((or delete buffer-substring-filters)
(save-excursion
(/ (float (- (nth 3 edges) (nth 1 edges))) dlh)))
;; Returns non-nil if partial move was done.
-(defun line-move-partial (arg noerror to-end)
+(defun line-move-partial (arg noerror &optional _to-end)
(if (< arg 0)
;; Move backward (up).
;; If already vscrolled, reduce vscroll
;; discrepancies between that and DLH.
(if (and rowh rbot (>= (- (+ rowh rbot) winh) 1))
(set-window-vscroll nil dlh t))
- (line-move-1 arg noerror to-end)
+ (line-move-1 arg noerror)
t)
;; If there are lines above the last line, scroll-up one line.
((and vpos (> vpos 0))
;; scrolling with cursor motion. But so far we don't have
;; a cleaner solution to the problem of making C-n do something
;; useful given a tall image.
-(defun line-move (arg &optional noerror to-end try-vscroll)
+(defun line-move (arg &optional noerror _to-end try-vscroll)
"Move forward ARG lines.
If NOERROR, don't signal an error if we can't move ARG lines.
TO-END is unused.
`auto-window-vscroll' or TRY-VSCROLL is nil, this function will
not vscroll."
(if noninteractive
- (line-move-1 arg noerror to-end)
+ (line-move-1 arg noerror)
(unless (and auto-window-vscroll try-vscroll
;; Only vscroll for single line moves
(= (abs arg) 1)
;; But don't vscroll in a keyboard macro.
(not defining-kbd-macro)
(not executing-kbd-macro)
- (line-move-partial arg noerror to-end))
+ (line-move-partial arg noerror))
(set-window-vscroll nil 0 t)
(if (and line-move-visual
;; Display-based column are incompatible with goal-column.
(set-window-vscroll
nil
(- lh dlh) t))))
- (line-move-1 arg noerror to-end)))))
+ (line-move-1 arg noerror)))))
;; Display-based alternative to line-move-1.
;; Arg says how many lines to move. The value is t if we can move the
(setq temporary-goal-column
(cons (/ (float x-pos)
(frame-char-width))
- hscroll))))))
+ hscroll)))
+ (executing-kbd-macro
+ ;; When we move beyond the first/last character visible in
+ ;; the window, posn-at-point will return nil, so we need to
+ ;; approximate the goal column as below.
+ (setq temporary-goal-column
+ (mod (current-column) (window-text-width)))))))
(if target-hscroll
(set-window-hscroll (selected-window) target-hscroll))
;; vertical-motion can move more than it was asked to if it moves
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
-;;; Commentary:
-
-;;; Code:
-
;; Beware: while this file has tag `utf-8', before it's compiled, it gets
;; loaded as "raw-text", so non-ASCII chars won't work right during bootstrap.
-(defmacro declare-function (_fn _file &optional _arglist _fileonly)
+
+;; declare-function's args use &rest, not &optional, for compatibility
+;; with byte-compile-macroexpand-declare-function.
+
+(defmacro declare-function (_fn _file &rest _args)
"Tell the byte-compiler that function FN is defined, in FILE.
-Optional ARGLIST is the argument list used by the function.
The FILE argument is not used by the byte-compiler, but by the
`check-declare' package, which checks that FILE contains a
-definition for FN. ARGLIST is used by both the byte-compiler
-and `check-declare' to check for consistency.
+definition for FN.
FILE can be either a Lisp file (in which case the \".el\"
extension is optional), or a C file. C files are expanded
`check-declare' will check such files if they are found, and skip
them without error if they are not.
-FILEONLY non-nil means that `check-declare' will only check that
-FILE exists, not that it defines FN. This is intended for
-function-definitions that `check-declare' does not recognize, e.g.
-`defstruct'.
+Optional ARGLIST specifies FN's arguments, or is t to not specify
+FN's arguments. An omitted ARGLIST defaults to t, not nil: a nil
+ARGLIST specifies an empty argument list, and an explicit t
+ARGLIST is a placeholder that allows supplying a later arg.
-To specify a value for FILEONLY without passing an argument list,
-set ARGLIST to t. This is necessary because nil means an
-empty argument list, rather than an unspecified one.
+Optional FILEONLY non-nil means that `check-declare' will check
+only that FILE exists, not that it defines FN. This is intended
+for function definitions that `check-declare' does not recognize,
+e.g., `defstruct'.
Note that for the purposes of `check-declare', this statement
must be the first non-whitespace on a line.
For more information, see Info node `(elisp)Declaring Functions'."
+ (declare (advertised-calling-convention
+ (fn file &optional arglist fileonly) nil))
;; Does nothing - byte-compile-declare-function does the work.
nil)
;;;; Basic Lisp macros.
(defalias 'not 'null)
+(defalias 'sxhash 'sxhash-equal)
(defmacro noreturn (form)
"Evaluate FORM, expecting it not to return.
alist)
(defun alist-get (key alist &optional default remove)
- "Get the value associated to KEY in ALIST.
- DEFAULT is the value to return if KEY is not found in ALIST.
- REMOVE, if non-nil, means that when setting this element, we should
- remove the entry if the new value is `eql' to DEFAULT."
+ "Return the value associated with KEY in ALIST, using `assq'.
+ If KEY is not found in ALIST, return DEFAULT.
+
+ This is a generalized variable suitable for use with `setf'.
+ When using it to set a value, optional argument REMOVE non-nil
+ means to remove KEY from ALIST if the new value is `eql' to DEFAULT."
(ignore remove) ;;Silence byte-compiler.
(let ((x (assq key alist)))
(if x (cdr x) default)))
Note that if KEY has a local binding in the current buffer,
that local binding will continue to shadow any global binding
that you make with this function."
- (interactive "KSet key globally: \nCSet key %s to command: ")
+ (interactive
+ (let* ((menu-prompting nil)
+ (key (read-key-sequence "Set key globally: ")))
+ (list key
+ (read-command (format "Set key %s to command: "
+ (key-description key))))))
(or (vectorp key) (stringp key)
(signal 'wrong-type-argument (list 'arrayp key)))
(define-key (current-global-map) key command))
(declare (indent 2) (debug (form sexp body))
(obsolete "use a <foo>-function variable modified by `add-function'."
"24.4"))
+ `(subr--with-wrapper-hook-no-warnings ,hook ,args ,@body))
+
+(defmacro subr--with-wrapper-hook-no-warnings (hook args &rest body)
+ "Like (with-wrapper-hook HOOK ARGS BODY), but without warnings."
;; We need those two gensyms because CL's lexical scoping is not available
;; for function arguments :-(
(let ((funs (make-symbol "funs"))
(make-variable-buffer-local 'delayed-mode-hooks)
(put 'delay-mode-hooks 'permanent-local t)
+(defvar delayed-after-hook-forms nil
+ "List of delayed :after-hook forms waiting to be run.
+These forms come from `define-derived-mode'.")
+(make-variable-buffer-local 'delayed-after-hook-forms)
+
(defvar change-major-mode-after-body-hook nil
"Normal hook run in major mode functions, before the mode hooks.")
(defun run-mode-hooks (&rest hooks)
"Run mode hooks `delayed-mode-hooks' and HOOKS, or delay HOOKS.
-If the variable `delay-mode-hooks' is non-nil, does not run any hooks,
+Call `hack-local-variables' to set up file local and directory local
+variables.
+
+If the variable `delay-mode-hooks' is non-nil, does not do anything,
just adds the HOOKS to the list `delayed-mode-hooks'.
Otherwise, runs hooks in the sequence: `change-major-mode-after-body-hook',
-`delayed-mode-hooks' (in reverse order), HOOKS, and finally
-`after-change-major-mode-hook'. Major mode functions should use
-this instead of `run-hooks' when running their FOO-mode-hook."
+`delayed-mode-hooks' (in reverse order), HOOKS, then runs
+`hack-local-variables', runs the hook `after-change-major-mode-hook', and
+finally evaluates the forms in `delayed-after-hook-forms' (see
+`define-derived-mode').
+
+Major mode functions should use this instead of `run-hooks' when
+running their FOO-mode-hook."
(if delay-mode-hooks
;; Delaying case.
(dolist (hook hooks)
(setq hooks (nconc (nreverse delayed-mode-hooks) hooks))
(setq delayed-mode-hooks nil)
(apply 'run-hooks (cons 'change-major-mode-after-body-hook hooks))
- (run-hooks 'after-change-major-mode-hook)))
+ (if (buffer-file-name)
+ (with-demoted-errors "File local-variables error: %s"
+ (hack-local-variables 'no-mode)))
+ (run-hooks 'after-change-major-mode-hook)
+ (dolist (form (nreverse delayed-after-hook-forms))
+ (eval form))
+ (setq delayed-after-hook-forms nil)))
(defmacro delay-mode-hooks (&rest body)
"Execute BODY, but delay any `run-mode-hooks'.
(message "%s%s" prompt (char-to-string char))
char))
+(defun read-multiple-choice (prompt choices)
+ "Ask user a multiple choice question.
+PROMPT should be a string that will be displayed as the prompt.
+
+CHOICES is an alist where the first element in each entry is a
+character to be entered, the second element is a short name for
+the entry to be displayed while prompting (if there's room, it
+might be shortened), and the third, optional entry is a longer
+explanation that will be displayed in a help buffer if the user
+requests more help.
+
+This function translates user input into responses by consulting
+the bindings in `query-replace-map'; see the documentation of
+that variable for more information. In this case, the useful
+bindings are `recenter', `scroll-up', and `scroll-down'. If the
+user enters `recenter', `scroll-up', or `scroll-down' responses,
+perform the requested window recentering or scrolling and ask
+again.
+
+The return value is the matching entry from the CHOICES list.
+
+Usage example:
+
+\(read-multiple-choice \"Continue connecting?\"
+ '((?a \"always\")
+ (?s \"session only\")
+ (?n \"no\")))"
+ (let* ((altered-names nil)
+ (full-prompt
+ (format
+ "%s (%s): "
+ prompt
+ (mapconcat
+ (lambda (elem)
+ (let* ((name (cadr elem))
+ (pos (seq-position name (car elem)))
+ (altered-name
+ (cond
+ ;; Not in the name string.
+ ((not pos)
+ (format "[%c] %s" (car elem) name))
+ ;; The prompt character is in the name, so highlight
+ ;; it on graphical terminals...
+ ((display-supports-face-attributes-p
+ '(:underline t) (window-frame))
+ (setq name (copy-sequence name))
+ (put-text-property pos (1+ pos)
+ 'face 'read-multiple-choice-face
+ name)
+ name)
+ ;; And put it in [bracket] on non-graphical terminals.
+ (t
+ (concat
+ (substring name 0 pos)
+ "["
+ (upcase (substring name pos (1+ pos)))
+ "]"
+ (substring name (1+ pos)))))))
+ (push (cons (car elem) altered-name)
+ altered-names)
+ altered-name))
+ (append choices '((?? "?")))
+ ", ")))
+ tchar buf wrong-char answer)
+ (save-window-excursion
+ (save-excursion
+ (while (not tchar)
+ (message "%s%s"
+ (if wrong-char
+ "Invalid choice. "
+ "")
+ full-prompt)
+ (setq tchar
+ (if (and (display-popup-menus-p)
+ last-input-event ; not during startup
+ (listp last-nonmenu-event)
+ use-dialog-box)
+ (x-popup-dialog
+ t
+ (cons prompt
+ (mapcar
+ (lambda (elem)
+ (cons (capitalize (cadr elem))
+ (car elem)))
+ choices)))
+ (condition-case nil
+ (let ((cursor-in-echo-area t))
+ (read-char))
+ (error nil))))
+ (setq answer (lookup-key query-replace-map (vector tchar) t))
+ (setq tchar
+ (cond
+ ((eq answer 'recenter)
+ (recenter) t)
+ ((eq answer 'scroll-up)
+ (ignore-errors (scroll-up-command)) t)
+ ((eq answer 'scroll-down)
+ (ignore-errors (scroll-down-command)) t)
+ ((eq answer 'scroll-other-window)
+ (ignore-errors (scroll-other-window)) t)
+ ((eq answer 'scroll-other-window-down)
+ (ignore-errors (scroll-other-window-down)) t)
+ (t tchar)))
+ (when (eq tchar t)
+ (setq wrong-char nil
+ tchar nil))
+ ;; The user has entered an invalid choice, so display the
+ ;; help messages.
+ (when (and (not (eq tchar nil))
+ (not (assq tchar choices)))
+ (setq wrong-char (not (memq tchar '(?? ?\C-h)))
+ tchar nil)
+ (when wrong-char
+ (ding))
+ (with-help-window (setq buf (get-buffer-create
+ "*Multiple Choice Help*"))
+ (with-current-buffer buf
+ (erase-buffer)
+ (pop-to-buffer buf)
+ (insert prompt "\n\n")
+ (let* ((columns (/ (window-width) 25))
+ (fill-column 21)
+ (times 0)
+ (start (point)))
+ (dolist (elem choices)
+ (goto-char start)
+ (unless (zerop times)
+ (if (zerop (mod times columns))
+ ;; Go to the next "line".
+ (goto-char (setq start (point-max)))
+ ;; Add padding.
+ (while (not (eobp))
+ (end-of-line)
+ (insert (make-string (max (- (* (mod times columns)
+ (+ fill-column 4))
+ (current-column))
+ 0)
+ ?\s))
+ (forward-line 1))))
+ (setq times (1+ times))
+ (let ((text
+ (with-temp-buffer
+ (insert (format
+ "%c: %s\n"
+ (car elem)
+ (cdr (assq (car elem) altered-names))))
+ (fill-region (point-min) (point-max))
+ (when (nth 2 elem)
+ (let ((start (point)))
+ (insert (nth 2 elem))
+ (unless (bolp)
+ (insert "\n"))
+ (fill-region start (point-max))))
+ (buffer-string))))
+ (goto-char start)
+ (dolist (line (split-string text "\n"))
+ (end-of-line)
+ (if (bolp)
+ (insert line "\n")
+ (insert line))
+ (forward-line 1)))))))))))
+ (when (buffer-live-p buf)
+ (kill-buffer buf))
+ (assq tchar choices)))
+
(defun sit-for (seconds &optional nodisp obsolete)
"Redisplay, then wait for SECONDS seconds. Stop when input is available.
SECONDS may be a floating-point value.
`inhibit-field-text-motion' is non-nil.
This function is like `forward-word', but it is not affected
-by `find-word-boundary-function-table' (as set up by
-e.g. `subword-mode'). It is also not interactive."
+by `find-word-boundary-function-table'. It is also not interactive."
(let ((find-word-boundary-function-table
(if (char-table-p word-move-empty-char-table)
word-move-empty-char-table
If ARG is omitted or nil, move point backward one word.
This function is like `forward-word', but it is not affected
-by `find-word-boundary-function-table' (as set up by
-e.g. `subword-mode'). It is also not interactive."
+by `find-word-boundary-function-table'. It is also not interactive."
(let ((find-word-boundary-function-table
(if (char-table-p word-move-empty-char-table)
word-move-empty-char-table
\f
;;; Misc.
+
+(defvar definition-prefixes (make-hash-table :test 'equal)
+ "Hash table mapping prefixes to the files in which they're used.
+This can be used to automatically fetch not-yet-loaded definitions.
+More specifically, if there is a value of the form (FILES...) for a string PREFIX
+it means that the FILES define variables or functions with names that start
+with PREFIX.
+
+Note that it does not imply that all definitions starting with PREFIX can
+be found in those files. E.g. if prefix is \"gnus-article-\" there might
+still be definitions of the form \"gnus-article-toto-titi\" in other files, which would
+presumably appear in this table under another prefix such as \"gnus-\"
+or \"gnus-article-toto-\".")
+
+(defun register-definition-prefixes (file prefixes)
+ "Register that FILE uses PREFIXES."
+ (dolist (prefix prefixes)
+ (puthash prefix (cons file (gethash prefix definition-prefixes))
+ definition-prefixes)))
+
(defconst menu-bar-separator '("--")
"Separator for menus.")
specbind (Qinhibit_redisplay, Qnil);
redisplaying_p = 0;
+ /* This variable stores buffers that have changed so that an undo
+ boundary can be added. specbind this so that changes in the
+ recursive edit will not result in undo boundaries in buffers
+ changed before we entered there recursive edit.
+ See Bug #23632.
+ */
+ specbind (Qundo_auto__undoably_changed_buffers, Qnil);
+
val = command_loop ();
if (EQ (val, Qt))
Fsignal (Qquit, Qnil);
{
Lisp_Object c = Qnil;
sys_jmp_buf save_jump;
- KBOARD *kb IF_LINT (= NULL);
+ KBOARD *kb;
start:
Lisp_Object prev_event,
bool *used_mouse_menu)
{
-#define MAX_ENCODED_BYTES 16
#ifndef WINDOWSNT
+#define MAX_ENCODED_BYTES 16
Lisp_Object events[MAX_ENCODED_BYTES];
int n = 0;
#endif
Lisp_Object prev_event,
bool *used_mouse_menu, struct timespec *end_time)
{
- Lisp_Object c;
+ Lisp_Object NONVOLATILE c;
ptrdiff_t jmpcount;
sys_jmp_buf local_getcjmp;
sys_jmp_buf save_jump;
last_input_event = c;
call4 (Qcommand_execute, tem, Qnil, Fvector (1, &last_input_event), Qt);
- if (CONSP (c) && EQ (XCAR (c), Qselect_window) && !end_time)
+ if (CONSP (c)
+ && (EQ (XCAR (c), Qselect_window)
+#ifdef HAVE_DBUS
+ || EQ (XCAR (c), Qdbus_event)
+#endif
+#ifdef USE_FILE_NOTIFY
+ || EQ (XCAR (c), Qfile_notify)
+#endif
+ || EQ (XCAR (c), Qconfig_changed_event))
+ && !end_time)
/* We stopped being idle for this event; undo that. This
prevents automatic window selection (under
mouse-autoselect-window) from acting as a real input event, for
kbd_fetch_ptr = event + 1;
}
#endif
+
+#ifdef HAVE_NTGUI
+ else if (event->kind == END_SESSION_EVENT)
+ {
+ /* Make an event (end-session). */
+ obj = list1 (Qend_session);
+ kbd_fetch_ptr = event + 1;
+ }
+#endif
+
#if defined (HAVE_X11) || defined (HAVE_NTGUI) \
|| defined (HAVE_NS)
else if (event->kind == ICONIFY_EVENT)
}
#endif
-
#if defined HAVE_INOTIFY || defined HAVE_KQUEUE || defined HAVE_GFILENOTIFY
case FILE_NOTIFY_EVENT:
{
the kbd_buffer can really hold. That may prevent loss
of characters on some systems when input is stuffed at us. */
unsigned char cbuf[KBD_BUFFER_SIZE - 1];
- int n_to_read, i;
+#ifndef WINDOWSNT
+ int n_to_read;
+#endif
+ int i;
struct tty_display_info *tty = terminal->display_info.tty;
int nread = 0;
#ifdef subprocesses
/* The length of the echo buffer when we started reading, and
the length of this_command_keys when we started reading. */
- ptrdiff_t echo_start IF_LINT (= 0);
+ ptrdiff_t echo_start UNINIT;
ptrdiff_t keys_start;
Lisp_Object current_binding = Qnil;
While we're reading, we keep the event here. */
Lisp_Object delayed_switch_frame;
- Lisp_Object original_uppercase IF_LINT (= Qnil);
+ Lisp_Object original_uppercase UNINIT;
int original_uppercase_position = -1;
/* Gets around Microsoft compiler limitations. */
while those allow us to restart the entire key sequence,
echo_local_start and keys_local_start allow us to throw away
just one key. */
- ptrdiff_t echo_local_start IF_LINT (= 0);
+ ptrdiff_t echo_local_start UNINIT;
int keys_local_start;
Lisp_Object new_binding;
is used. Note that [Enter] is not echoed by dos. */
cursor_to (SELECTED_FRAME (), 0, 0);
#endif
+
+ write_stdout ("Emacs is resuming after an emergency escape.\n");
+
/* It doesn't work to autosave while GC is in progress;
the code used for auto-saving doesn't cope with the mark bit. */
if (!gc_in_progress)
DEFSYM (Qpost_command_hook, "post-command-hook");
DEFSYM (Qundo_auto__add_boundary, "undo-auto--add-boundary");
+ DEFSYM (Qundo_auto__undoably_changed_buffers,
+ "undo-auto--undoably-changed-buffers");
DEFSYM (Qdeferred_action_function, "deferred-action-function");
DEFSYM (Qdelayed_warnings_hook, "delayed-warnings-hook");
#ifdef HAVE_NTGUI
DEFSYM (Qlanguage_change, "language-change");
+ DEFSYM (Qend_session, "end-session");
#endif
#ifdef HAVE_DBUS
variable are `sigusr1' and `sigusr2'. */);
Vdebug_on_event = intern_c_string ("sigusr2");
+ DEFVAR_BOOL ("attempt-stack-overflow-recovery",
+ attempt_stack_overflow_recovery,
+ doc: /* If non-nil, attempt to recover from C stack
+overflow. This recovery is unsafe and may lead to deadlocks or data
+corruption, but it usually works and may preserve modified buffers
+that would otherwise be lost. If nil, treat stack overflow like any
+other kind of crash. */);
+ attempt_stack_overflow_recovery = true;
+
+ DEFVAR_BOOL ("attempt-orderly-shutdown-on-fatal-signal",
+ attempt_orderly_shutdown_on_fatal_signal,
+ doc: /* If non-nil, attempt to perform an orderly
+shutdown when Emacs receives a fatal signal (e.g., a crash).
+This cleanup is unsafe and may lead to deadlocks or data corruption,
+but it usually works and may preserve modified buffers that would
+otherwise be lost. If nil, crash immediately in response to fatal
+signals. */);
+ attempt_orderly_shutdown_on_fatal_signal = true;
+
/* Create the initial keyboard. Qt means 'unset'. */
initial_kboard = allocate_kboard (Qt);
}
initial_define_lispy_key (Vspecial_event_map, "delete-frame",
"handle-delete-frame");
+#ifdef HAVE_NTGUI
+ initial_define_lispy_key (Vspecial_event_map, "end-session",
+ "kill-emacs");
+#endif
initial_define_lispy_key (Vspecial_event_map, "ns-put-working-text",
"ns-put-working-text");
initial_define_lispy_key (Vspecial_event_map, "ns-unput-working-text",
return (flags >> 19) & 1;
}
static bool
+SYNTAX_FLAGS_COMSTARTEND_FIRST (int flags)
+{
+ return (flags & 0x50000) != 0;
+}
+static bool
SYNTAX_FLAGS_PREFIX (int flags)
{
return (flags >> 20) & 1;
ptrdiff_t comstr_start; /* Position of last comment/string starter. */
Lisp_Object levelstarts; /* Char numbers of starts-of-expression
of levels (starting from outermost). */
+ int prev_syntax; /* Syntax of previous position scanned, when
+ that position (potentially) holds the first char
+ of a 2-char construct, i.e. comment delimiter
+ or Sescape, etc. Smax otherwise. */
};
\f
/* These variables are a cache for finding the start of a defun.
static Lisp_Object scan_lists (EMACS_INT, EMACS_INT, EMACS_INT, bool);
static void scan_sexps_forward (struct lisp_parse_state *,
ptrdiff_t, ptrdiff_t, ptrdiff_t, EMACS_INT,
- bool, Lisp_Object, int);
+ bool, int);
+static void internalize_parse_state (Lisp_Object, struct lisp_parse_state *);
static bool in_classes (int, Lisp_Object);
static void parse_sexp_propertize (ptrdiff_t charpos);
ptrdiff_t comment_end = from;
ptrdiff_t comment_end_byte = from_byte;
ptrdiff_t comstart_pos = 0;
- ptrdiff_t comstart_byte IF_LINT (= 0);
+ ptrdiff_t comstart_byte;
/* Place where the containing defun starts,
or 0 if we didn't come across it yet. */
ptrdiff_t defun_start = 0;
}
do
{
+ internalize_parse_state (Qnil, &state);
scan_sexps_forward (&state,
defun_start, defun_start_byte,
comment_end, TYPE_MINIMUM (EMACS_INT),
- 0, Qnil, 0);
+ 0, 0);
defun_start = comment_end;
if (!adjusted)
{
int c;
char fastmap[0400];
/* Store the ranges of non-ASCII characters. */
- int *char_ranges IF_LINT (= NULL);
+ int *char_ranges UNINIT;
int n_char_ranges = 0;
bool negate = 0;
ptrdiff_t i, i_byte;
PREV_SYNTAX is the SYNTAX_WITH_FLAGS of the previous character
(or 0 If the search cannot start in the middle of a two-character).
- If successful, return true and store the charpos of the comment's end
- into *CHARPOS_PTR and the corresponding bytepos into *BYTEPOS_PTR.
- Else, return false and store the charpos STOP into *CHARPOS_PTR, the
- corresponding bytepos into *BYTEPOS_PTR and the current nesting
- (as defined for state.incomment) in *INCOMMENT_PTR.
+ If successful, return true and store the charpos of the comment's
+ end into *CHARPOS_PTR and the corresponding bytepos into
+ *BYTEPOS_PTR. Else, return false and store the charpos STOP into
+ *CHARPOS_PTR, the corresponding bytepos into *BYTEPOS_PTR and the
+ current nesting (as defined for state->incomment) in
+ *INCOMMENT_PTR. Should the last character scanned in an incomplete
+ comment be a possible first character of a two character construct,
+ we store its SYNTAX_WITH_FLAGS into *last_syntax_ptr. Otherwise,
+ we store Smax into *last_syntax_ptr.
The comment end is the last character of the comment rather than the
character just after the comment.
forw_comment (ptrdiff_t from, ptrdiff_t from_byte, ptrdiff_t stop,
EMACS_INT nesting, int style, int prev_syntax,
ptrdiff_t *charpos_ptr, ptrdiff_t *bytepos_ptr,
- EMACS_INT *incomment_ptr)
+ EMACS_INT *incomment_ptr, int *last_syntax_ptr)
{
register int c, c1;
register enum syntaxcode code;
/* Enter the loop in the middle so that we find
a 2-char comment ender if we start in the middle of it. */
syntax = prev_syntax;
- if (syntax != 0) goto forw_incomment;
+ code = syntax & 0xff;
+ if (syntax != 0 && from < stop) goto forw_incomment;
while (1)
{
*incomment_ptr = nesting;
*charpos_ptr = from;
*bytepos_ptr = from_byte;
+ *last_syntax_ptr =
+ (code == Sescape || code == Scharquote
+ || SYNTAX_FLAGS_COMEND_FIRST (syntax)
+ || (nesting > 0
+ && SYNTAX_FLAGS_COMSTART_FIRST (syntax)))
+ ? syntax : Smax ;
return 0;
}
c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
SYNTAX_FLAGS_COMMENT_NESTED (other_syntax))
? nesting > 0 : nesting < 0))
{
- if (--nesting <= 0)
+ syntax = Smax; /* So that "|#" (lisp) can not return
+ the syntax of "#" in *last_syntax_ptr. */
+ if (--nesting <= 0)
/* We have encountered a comment end of the same style
as the comment sequence which began this comment section. */
break;
/* We have encountered a nested comment of the same style
as the comment sequence which began this comment section. */
{
+ syntax = Smax; /* So that "#|#" isn't also a comment ender. */
INC_BOTH (from, from_byte);
UPDATE_SYNTAX_TABLE_FORWARD (from);
nesting++;
}
*charpos_ptr = from;
*bytepos_ptr = from_byte;
+ *last_syntax_ptr = Smax; /* Any syntactic power the last byte had is
+ used up. */
return 1;
}
EMACS_INT count1;
ptrdiff_t out_charpos, out_bytepos;
EMACS_INT dummy;
+ int dummy2;
CHECK_NUMBER (count);
count1 = XINT (count);
}
/* We're at the start of a comment. */
found = forw_comment (from, from_byte, stop, comnested, comstyle, 0,
- &out_charpos, &out_bytepos, &dummy);
+ &out_charpos, &out_bytepos, &dummy, &dummy2);
from = out_charpos; from_byte = out_bytepos;
if (!found)
{
ptrdiff_t from_byte;
ptrdiff_t out_bytepos, out_charpos;
EMACS_INT dummy;
+ int dummy2;
bool multibyte_symbol_p = sexpflag && multibyte_syntax_as_symbol;
if (depth > 0) min_depth = 0;
UPDATE_SYNTAX_TABLE_FORWARD (from);
found = forw_comment (from, from_byte, stop,
comnested, comstyle, 0,
- &out_charpos, &out_bytepos, &dummy);
+ &out_charpos, &out_bytepos, &dummy,
+ &dummy2);
from = out_charpos, from_byte = out_bytepos;
if (!found)
{
}
\f
/* Parse forward from FROM / FROM_BYTE to END,
- assuming that FROM has state OLDSTATE (nil means FROM is start of function),
+ assuming that FROM has state STATE,
and return a description of the state of the parse at END.
If STOPBEFORE, stop at the start of an atom.
If COMMENTSTOP is 1, stop at the start of a comment.
after the beginning of a string, or after the end of a string. */
static void
-scan_sexps_forward (struct lisp_parse_state *stateptr,
+scan_sexps_forward (struct lisp_parse_state *state,
ptrdiff_t from, ptrdiff_t from_byte, ptrdiff_t end,
EMACS_INT targetdepth, bool stopbefore,
- Lisp_Object oldstate, int commentstop)
+ int commentstop)
{
- struct lisp_parse_state state;
enum syntaxcode code;
int c1;
bool comnested;
Lisp_Object tem;
ptrdiff_t prev_from; /* Keep one character before FROM. */
ptrdiff_t prev_from_byte;
- int prev_from_syntax;
+ int prev_from_syntax, prev_prev_from_syntax;
bool boundary_stop = commentstop == -1;
bool nofence;
bool found;
do { prev_from = from; \
prev_from_byte = from_byte; \
temp = FETCH_CHAR_AS_MULTIBYTE (prev_from_byte); \
+ prev_prev_from_syntax = prev_from_syntax; \
prev_from_syntax = SYNTAX_WITH_FLAGS (temp); \
INC_BOTH (from, from_byte); \
if (from < end) \
immediate_quit = 1;
QUIT;
- if (NILP (oldstate))
- {
- depth = 0;
- state.instring = -1;
- state.incomment = 0;
- state.comstyle = 0; /* comment style a by default. */
- state.comstr_start = -1; /* no comment/string seen. */
- }
- else
- {
- tem = Fcar (oldstate);
- if (!NILP (tem))
- depth = XINT (tem);
- else
- depth = 0;
-
- oldstate = Fcdr (oldstate);
- oldstate = Fcdr (oldstate);
- oldstate = Fcdr (oldstate);
- tem = Fcar (oldstate);
- /* Check whether we are inside string_fence-style string: */
- state.instring = (!NILP (tem)
- ? (CHARACTERP (tem) ? XFASTINT (tem) : ST_STRING_STYLE)
- : -1);
-
- oldstate = Fcdr (oldstate);
- tem = Fcar (oldstate);
- state.incomment = (!NILP (tem)
- ? (INTEGERP (tem) ? XINT (tem) : -1)
- : 0);
-
- oldstate = Fcdr (oldstate);
- tem = Fcar (oldstate);
- start_quoted = !NILP (tem);
+ depth = state->depth;
+ start_quoted = state->quoted;
+ prev_prev_from_syntax = Smax;
+ prev_from_syntax = state->prev_syntax;
- /* if the eighth element of the list is nil, we are in comment
- style a. If it is non-nil, we are in comment style b */
- oldstate = Fcdr (oldstate);
- oldstate = Fcdr (oldstate);
- tem = Fcar (oldstate);
- state.comstyle = (NILP (tem)
- ? 0
- : (RANGED_INTEGERP (0, tem, ST_COMMENT_STYLE)
- ? XINT (tem)
- : ST_COMMENT_STYLE));
-
- oldstate = Fcdr (oldstate);
- tem = Fcar (oldstate);
- state.comstr_start =
- RANGED_INTEGERP (PTRDIFF_MIN, tem, PTRDIFF_MAX) ? XINT (tem) : -1;
- oldstate = Fcdr (oldstate);
- tem = Fcar (oldstate);
- while (!NILP (tem)) /* >= second enclosing sexps. */
- {
- Lisp_Object temhd = Fcar (tem);
- if (RANGED_INTEGERP (PTRDIFF_MIN, temhd, PTRDIFF_MAX))
- curlevel->last = XINT (temhd);
- if (++curlevel == endlevel)
- curlevel--; /* error ("Nesting too deep for parser"); */
- curlevel->prev = -1;
- curlevel->last = -1;
- tem = Fcdr (tem);
- }
+ tem = state->levelstarts;
+ while (!NILP (tem)) /* >= second enclosing sexps. */
+ {
+ Lisp_Object temhd = Fcar (tem);
+ if (RANGED_INTEGERP (PTRDIFF_MIN, temhd, PTRDIFF_MAX))
+ curlevel->last = XINT (temhd);
+ if (++curlevel == endlevel)
+ curlevel--; /* error ("Nesting too deep for parser"); */
+ curlevel->prev = -1;
+ curlevel->last = -1;
+ tem = Fcdr (tem);
}
- state.quoted = 0;
- mindepth = depth;
-
curlevel->prev = -1;
curlevel->last = -1;
- SETUP_SYNTAX_TABLE (prev_from, 1);
- temp = FETCH_CHAR (prev_from_byte);
- prev_from_syntax = SYNTAX_WITH_FLAGS (temp);
- UPDATE_SYNTAX_TABLE_FORWARD (from);
+ state->quoted = 0;
+ mindepth = depth;
+
+ SETUP_SYNTAX_TABLE (from, 1);
/* Enter the loop at a place appropriate for initial state. */
- if (state.incomment)
+ if (state->incomment)
goto startincomment;
- if (state.instring >= 0)
+ if (state->instring >= 0)
{
- nofence = state.instring != ST_STRING_STYLE;
+ nofence = state->instring != ST_STRING_STYLE;
if (start_quoted)
goto startquotedinstring;
goto startinstring;
while (from < end)
{
int syntax;
- INC_FROM;
- code = prev_from_syntax & 0xff;
- if (from < end
- && SYNTAX_FLAGS_COMSTART_FIRST (prev_from_syntax)
+ if (SYNTAX_FLAGS_COMSTART_FIRST (prev_from_syntax)
&& (c1 = FETCH_CHAR (from_byte),
syntax = SYNTAX_WITH_FLAGS (c1),
SYNTAX_FLAGS_COMSTART_SECOND (syntax)))
/* Record the comment style we have entered so that only
the comment-end sequence of the same style actually
terminates the comment section. */
- state.comstyle
+ state->comstyle
= SYNTAX_FLAGS_COMMENT_STYLE (syntax, prev_from_syntax);
comnested = (SYNTAX_FLAGS_COMMENT_NESTED (prev_from_syntax)
| SYNTAX_FLAGS_COMMENT_NESTED (syntax));
- state.incomment = comnested ? 1 : -1;
- state.comstr_start = prev_from;
+ state->incomment = comnested ? 1 : -1;
+ state->comstr_start = prev_from;
INC_FROM;
+ prev_from_syntax = Smax; /* the syntax has already been
+ "used up". */
code = Scomment;
}
- else if (code == Scomment_fence)
- {
- /* Record the comment style we have entered so that only
- the comment-end sequence of the same style actually
- terminates the comment section. */
- state.comstyle = ST_COMMENT_STYLE;
- state.incomment = -1;
- state.comstr_start = prev_from;
- code = Scomment;
- }
- else if (code == Scomment)
- {
- state.comstyle = SYNTAX_FLAGS_COMMENT_STYLE (prev_from_syntax, 0);
- state.incomment = (SYNTAX_FLAGS_COMMENT_NESTED (prev_from_syntax) ?
- 1 : -1);
- state.comstr_start = prev_from;
- }
+ else
+ {
+ INC_FROM;
+ code = prev_from_syntax & 0xff;
+ if (code == Scomment_fence)
+ {
+ /* Record the comment style we have entered so that only
+ the comment-end sequence of the same style actually
+ terminates the comment section. */
+ state->comstyle = ST_COMMENT_STYLE;
+ state->incomment = -1;
+ state->comstr_start = prev_from;
+ code = Scomment;
+ }
+ else if (code == Scomment)
+ {
+ state->comstyle = SYNTAX_FLAGS_COMMENT_STYLE (prev_from_syntax, 0);
+ state->incomment = (SYNTAX_FLAGS_COMMENT_NESTED (prev_from_syntax) ?
+ 1 : -1);
+ state->comstr_start = prev_from;
+ }
+ }
if (SYNTAX_FLAGS_PREFIX (prev_from_syntax))
continue;
case Scomment_fence: /* Can't happen because it's handled above. */
case Scomment:
- if (commentstop || boundary_stop) goto done;
+ if (commentstop || boundary_stop) goto done;
startincomment:
/* The (from == BEGV) test was to enter the loop in the middle so
that we find a 2-char comment ender even if we start in the
middle of it. We don't want to do that if we're just at the
beginning of the comment (think of (*) ... (*)). */
found = forw_comment (from, from_byte, end,
- state.incomment, state.comstyle,
- (from == BEGV || from < state.comstr_start + 3)
- ? 0 : prev_from_syntax,
- &out_charpos, &out_bytepos, &state.incomment);
+ state->incomment, state->comstyle,
+ from == BEGV ? 0 : prev_from_syntax,
+ &out_charpos, &out_bytepos, &state->incomment,
+ &prev_from_syntax);
from = out_charpos; from_byte = out_bytepos;
- /* Beware! prev_from and friends are invalid now.
- Luckily, the `done' doesn't use them and the INC_FROM
- sets them to a sane value without looking at them. */
+ /* Beware! prev_from and friends (except prev_from_syntax)
+ are invalid now. Luckily, the `done' doesn't use them
+ and the INC_FROM sets them to a sane value without
+ looking at them. */
if (!found) goto done;
INC_FROM;
- state.incomment = 0;
- state.comstyle = 0; /* reset the comment style */
- if (boundary_stop) goto done;
+ state->incomment = 0;
+ state->comstyle = 0; /* reset the comment style */
+ prev_from_syntax = Smax; /* For the comment closer */
+ if (boundary_stop) goto done;
break;
case Sopen:
case Sstring:
case Sstring_fence:
- state.comstr_start = from - 1;
+ state->comstr_start = from - 1;
if (stopbefore) goto stop; /* this arg means stop at sexp start */
curlevel->last = prev_from;
- state.instring = (code == Sstring
+ state->instring = (code == Sstring
? (FETCH_CHAR_AS_MULTIBYTE (prev_from_byte))
: ST_STRING_STYLE);
if (boundary_stop) goto done;
startinstring:
{
- nofence = state.instring != ST_STRING_STYLE;
+ nofence = state->instring != ST_STRING_STYLE;
while (1)
{
/* Check C_CODE here so that if the char has
a syntax-table property which says it is NOT
a string character, it does not end the string. */
- if (nofence && c == state.instring && c_code == Sstring)
+ if (nofence && c == state->instring && c_code == Sstring)
break;
switch (c_code)
}
}
string_end:
- state.instring = -1;
+ state->instring = -1;
curlevel->prev = curlevel->last;
INC_FROM;
if (boundary_stop) goto done;
stop: /* Here if stopping before start of sexp. */
from = prev_from; /* We have just fetched the char that starts it; */
from_byte = prev_from_byte;
+ prev_from_syntax = prev_prev_from_syntax;
goto done; /* but return the position before it. */
endquoted:
- state.quoted = 1;
+ state->quoted = 1;
done:
- state.depth = depth;
- state.mindepth = mindepth;
- state.thislevelstart = curlevel->prev;
- state.prevlevelstart
+ state->depth = depth;
+ state->mindepth = mindepth;
+ state->thislevelstart = curlevel->prev;
+ state->prevlevelstart
= (curlevel == levelstart) ? -1 : (curlevel - 1)->last;
- state.location = from;
- state.location_byte = from_byte;
- state.levelstarts = Qnil;
+ state->location = from;
+ state->location_byte = from_byte;
+ state->levelstarts = Qnil;
while (curlevel > levelstart)
- state.levelstarts = Fcons (make_number ((--curlevel)->last),
- state.levelstarts);
+ state->levelstarts = Fcons (make_number ((--curlevel)->last),
+ state->levelstarts);
+ state->prev_syntax = (SYNTAX_FLAGS_COMSTARTEND_FIRST (prev_from_syntax)
+ || state->quoted) ? prev_from_syntax : Smax;
immediate_quit = 0;
+}
+
+/* Convert a (lisp) parse state to the internal form used in
+ scan_sexps_forward. */
+static void
+internalize_parse_state (Lisp_Object external, struct lisp_parse_state *state)
+{
+ Lisp_Object tem;
+
+ if (NILP (external))
+ {
+ state->depth = 0;
+ state->instring = -1;
+ state->incomment = 0;
+ state->quoted = 0;
+ state->comstyle = 0; /* comment style a by default. */
+ state->comstr_start = -1; /* no comment/string seen. */
+ state->levelstarts = Qnil;
+ state->prev_syntax = Smax;
+ }
+ else
+ {
+ tem = Fcar (external);
+ if (!NILP (tem))
+ state->depth = XINT (tem);
+ else
+ state->depth = 0;
+
+ external = Fcdr (external);
+ external = Fcdr (external);
+ external = Fcdr (external);
+ tem = Fcar (external);
+ /* Check whether we are inside string_fence-style string: */
+ state->instring = (!NILP (tem)
+ ? (CHARACTERP (tem) ? XFASTINT (tem) : ST_STRING_STYLE)
+ : -1);
+
+ external = Fcdr (external);
+ tem = Fcar (external);
+ state->incomment = (!NILP (tem)
+ ? (INTEGERP (tem) ? XINT (tem) : -1)
+ : 0);
+
+ external = Fcdr (external);
+ tem = Fcar (external);
+ state->quoted = !NILP (tem);
- *stateptr = state;
+ /* if the eighth element of the list is nil, we are in comment
+ style a. If it is non-nil, we are in comment style b */
+ external = Fcdr (external);
+ external = Fcdr (external);
+ tem = Fcar (external);
+ state->comstyle = (NILP (tem)
+ ? 0
+ : (RANGED_INTEGERP (0, tem, ST_COMMENT_STYLE)
+ ? XINT (tem)
+ : ST_COMMENT_STYLE));
+
+ external = Fcdr (external);
+ tem = Fcar (external);
+ state->comstr_start =
+ RANGED_INTEGERP (PTRDIFF_MIN, tem, PTRDIFF_MAX) ? XINT (tem) : -1;
+ external = Fcdr (external);
+ tem = Fcar (external);
+ state->levelstarts = tem;
+
+ external = Fcdr (external);
+ tem = Fcar (external);
+ state->prev_syntax = NILP (tem) ? Smax : XINT (tem);
+ }
}
DEFUN ("parse-partial-sexp", Fparse_partial_sexp, Sparse_partial_sexp, 2, 6, 0,
point is set to where parsing stops.
If fifth arg OLDSTATE is omitted or nil,
parsing assumes that FROM is the beginning of a function.
+
Value is a list of elements describing final state of parsing:
0. depth in parens.
1. character address of start of innermost containing list; nil if none.
6. the minimum paren-depth encountered during this scan.
7. style of comment, if any.
8. character address of start of comment or string; nil if not in one.
- 9. Intermediate data for continuation of parsing (subject to change).
+ 9. List of positions of currently open parens, outermost first.
+10. When the last position scanned holds the first character of a
+ (potential) two character construct, the syntax of that position,
+ otherwise nil. That construct can be a two character comment
+ delimiter or an Escaped or Char-quoted character.
+11..... Possible further internal information used by `parse-partial-sexp'.
+
If third arg TARGETDEPTH is non-nil, parsing stops if the depth
in parentheses becomes equal to TARGETDEPTH.
-Fourth arg STOPBEFORE non-nil means stop when come to
+Fourth arg STOPBEFORE non-nil means stop when we come to
any character that starts a sexp.
Fifth arg OLDSTATE is a list like what this function returns.
It is used to initialize the state of the parse. Elements number 1, 2, 6
are ignored.
-Sixth arg COMMENTSTOP non-nil means stop at the start of a comment.
- If it is symbol `syntax-table', stop after the start of a comment or a
+Sixth arg COMMENTSTOP non-nil means stop after the start of a comment.
+ If it is the symbol `syntax-table', stop after the start of a comment or a
string, or after end of a comment or a string. */)
(Lisp_Object from, Lisp_Object to, Lisp_Object targetdepth,
Lisp_Object stopbefore, Lisp_Object oldstate, Lisp_Object commentstop)
target = TYPE_MINIMUM (EMACS_INT); /* We won't reach this depth. */
validate_region (&from, &to);
+ internalize_parse_state (oldstate, &state);
scan_sexps_forward (&state, XINT (from), CHAR_TO_BYTE (XINT (from)),
XINT (to),
- target, !NILP (stopbefore), oldstate,
+ target, !NILP (stopbefore),
(NILP (commentstop)
? 0 : (EQ (commentstop, Qsyntax_table) ? -1 : 1)));
SET_PT_BOTH (state.location, state.location_byte);
- return Fcons (make_number (state.depth),
+ return
+ Fcons (make_number (state.depth),
Fcons (state.prevlevelstart < 0
? Qnil : make_number (state.prevlevelstart),
Fcons (state.thislevelstart < 0
? Qsyntax_table
: make_number (state.comstyle))
: Qnil),
- Fcons (((state.incomment
- || (state.instring >= 0))
- ? make_number (state.comstr_start)
- : Qnil),
- Fcons (state.levelstarts, Qnil))))))))));
+ Fcons (((state.incomment
+ || (state.instring >= 0))
+ ? make_number (state.comstr_start)
+ : Qnil),
+ Fcons (state.levelstarts,
+ Fcons (state.prev_syntax == Smax
+ ? Qnil
+ : make_number (state.prev_syntax),
+ Qnil)))))))))));
}
\f
void
Vfind_word_boundary_function_table = Fmake_char_table (Qnil, Qnil);
DEFVAR_BOOL ("comment-end-can-be-escaped", Vcomment_end_can_be_escaped,
- doc: /* Non-nil means an escaped ender inside a comment doesn'tend the comment. */);
+ doc: /* Non-nil means an escaped ender inside a comment doesn't end the comment. */);
Vcomment_end_can_be_escaped = 0;
DEFSYM (Qcomment_end_can_be_escaped, "comment-end-can-be-escaped");
Fmake_variable_buffer_local (Qcomment_end_can_be_escaped);
(= 6
(undo-test-point-after-forward-kill))))
+ (defmacro simple-test-undo-with-switched-buffer (buffer &rest body)
+ (let ((before-buffer (make-symbol "before-buffer")))
+ `(let ((,before-buffer (current-buffer)))
+ (unwind-protect
+ (progn
+ (switch-to-buffer ,buffer)
+ ,@body)
+ (switch-to-buffer ,before-buffer)))))
+
+ ;; This tests for a regression in emacs 25.0 see bug #23632
+ (ert-deftest simple-test-undo-extra-boundary-in-tex ()
+ (should
+ (string=
+ ""
+ (simple-test-undo-with-switched-buffer
+ "temp.tex"
+ (latex-mode)
+ ;; This macro calls `latex-insert-block'
+ (execute-kbd-macro
+ (read-kbd-macro
+ "
+ C-c C-o ;; latex-insert-block
+ RET ;; newline
+ C-/ ;; undo
+ "
+ ))
+ (buffer-substring-no-properties
+ (point-min)
+ (point-max))))))
+
+
+
(provide 'simple-test)
;;; simple-test.el ends here