Merge from origin/emacs-29

12e26cc0c1 ; * admin/git-bisect-start: Add mistakenly ommitted commi...
9a751e0a38 ruby-mode: Support endless singleton method definitions too
ce7b7e5af3 Remove comment-start-skip preset in tree-sitter indentati...
c1e015ae32 Fix recent change in tramp-smb.el
cf1b771864 ; * etc/NEWS: Fix typos.
50d18bb6ea Make tramp-archive autoloads robust for older Emacs versions
3941cc29df ; Improve documentation of 'setopt'
6f88de109c ruby-mode: Support endless methods (bug#54702)
91dd893e34 ; * lisp/progmodes/sql.el (sql-product-interactive): Doc ...
138d9dc4cb * lisp/cus-edit.el (setopt--set): Warn instead of rasing ...
d1e0542f33 Allow customising windmove user options with an empty prefix
c2375e7791 Improve and extend admin/git-bisect-start
7cc2313eb0 Make 'rmail-summary-by-thread' faster
88e59b16cb ; Improve documentation of installing tree-sitter and gra...
897f33bf31 Fix the MS-DOS build
660e941235 Avoid crashes in PGTK build due to signal in 'note_mouse_...
0fc5fb2d05 Fix MS-Windows build broken by recent treesit.c changes
5b2e6d04ce Fix wrong capture in typescript-ts-mode (bug#60167)
cb8ccdd267 Add rust-ts-mode (Bug#60136)
9fcf764dd7 Indentation fixes for jsx/tsx (bug#60169)
69f2c71135 Fix treesit-query-validate problem with view-mode
1fc7535546 Use cursor API in treesit-node-parent
5f0286c0af Switch to use cursor API in treesit.c
a275e436df Add treesit_assume_true and treesit_cursor_helper
a54c7a8df0 Remove file-exists-in-trash-p
3e02029642 Update to Org 9.6-49-g47d129
4a8ff671b0 Don’t assume make-directory handler returns nil
44c83b239d Fix copy-directory bug when dest dir exists
bef1edc9ca make-directory now returns t if dir already exists
8a9579ca29 Use make-directory handlers uniformly
627e7e0243 Improve documentation of 'file-exists-p'
c9015ef55f Fix resizing of mini-windows by 'set-minibuffer-message'
5a245bc786 Prevent Abort dialogs from async-compiling jobs on Windows
4d1e4a4893 Fix 'window-max-chars-per-line' when there are no fringes
d65beb820c ; Revert "; * lisp/subr.el (internal--with-narrowing): Si...
1c0b90e5f7 ruby-mode: Recognize instance or global var as first arg ...
3356c0cb16 Fix end-of-defun in ruby-mode

# Conflicts:
#	etc/NEWS
This commit is contained in:
Stefan Kangas 2022-12-19 04:42:19 +01:00
commit 6c540e38f4
43 changed files with 2952 additions and 511 deletions

File diff suppressed because it is too large Load diff

View file

@ -129,8 +129,12 @@ This is a semi-automated way to find the revision that introduced a bug.
Browse 'git help bisect' for technical instructions.
It is recommended to start a bisection with the admin/git-bisect-start
script. This script prunes the branches that are the result of
merging external trees into the Emacs repository.
script. Using that script ensures that commits in branches that are
the result of merging external trees into the Emacs repository, as
well as certain commits on which Emacs fails to build, are skipped
during the bisection process. That script can also be executed
automatically when 'git bisect start' is called, with the help of a
wrapper script that is included in its commentary section.
* Maintaining ChangeLog history

View file

@ -895,10 +895,14 @@ permissions of the file itself.)
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
symbolic links, it returns @code{t} for a symbolic link
name only if the target file exists.
@cindex dangling symlinks, testing for existence
Directories are files, so @code{file-exists-p} can return @code{t}
when given a directory. However, because @code{file-exists-p} follows
symbolic links, it returns @code{t} for a symbolic link name only if
the target of the link exists; if your Lisp program needs to consider
@dfn{dangling symlinks} whose target doesn't exist as existing files,
use @code{file-attributes} (@pxref{File Attributes}) instead of
@code{file-exists-p}.
@end defun
@defun file-readable-p filename
@ -3205,6 +3209,9 @@ This command creates a directory named @var{dirname}. If
@var{parents} is non-@code{nil}, as is always the case in an
interactive call, that means to create the parent directories first,
if they don't already exist.
As a function, @code{make-directory} returns non-@code{nil} if @var{dirname}
already exists as a directory and @var{parents} is non-@code{nil},
and returns @code{nil} if it successfully created @var{dirname}.
@code{mkdir} is an alias for this.
@end deffn
@ -3378,7 +3385,6 @@ first, before handlers for jobs such as remote file access.
@code{load}, @code{lock-file},
@code{make-auto-save-file-name},
@code{make-directory},
@code{make-directory-internal},
@code{make-lock-file-name},
@code{make-nearby-temp-file},
@code{make-process},
@ -3440,7 +3446,6 @@ first, before handlers for jobs such as remote file access.
@code{load}, @code{lock-file},
@code{make-auto-save-file-name},
@code{make-direc@discretionary{}{}{}tory},
@code{make-direc@discretionary{}{}{}tory-internal},
@code{make-lock-file-name},
@code{make-nearby-temp-file},
@code{make-process},

View file

@ -4967,12 +4967,22 @@ first child where parent is @code{argument_list}, use
(match nil "argument_list" nil nil 0 0)
@end example
@item n-p-gp
Short for ``node-parent-grandparent'', this matcher is a function of 3
arguments: @var{node-type}, @var{parent-type}, and
@var{grandparent-type}. It returns a function that is called with 3
arguments: @var{node}, @var{parent}, and @var{bol}, and returns
non-@code{nil} if: (1) @var{node-type} matches @var{node}'s type, and
(2) @var{parent-type} matches @var{parent}'s type, and (3)
@var{grandparent-type} matches @var{parent}'s parent's type. If any
of @var{node-type}, @var{parent-type}, and @var{grandparent-type} is
@code{nil}, this function doesn't check for it.
@item comment-end
This matcher is a function that is called with 3 arguments:
@var{node}, @var{parent}, and @var{bol}, and returns non-@code{nil} if
point is before a comment ending token. Comment ending tokens are
defined by regular expression @code{treesit-comment-end}
(@pxref{Tree-sitter major modes, treesit-comment-end}).
defined by regular expression @code{comment-end-skip}
@item first-sibling
This anchor is a function that is called with 3 arguments: @var{node},
@ -5009,19 +5019,11 @@ This is useful as the beginning of the buffer is always at column 0.
@item comment-start
This anchor is a function that is called with 3 arguments: @var{node},
@var{parent}, and @var{bol}, and returns the position right after the
comment-start token. Comment-start tokens are defined by regular
expression @code{treesit-comment-start} (@pxref{Tree-sitter major
modes, treesit-comment-start}). This function assumes @var{parent} is
the comment node.
@item comment-start-skip
This anchor is a function that is called with 3 arguments: @var{node},
@var{parent}, and @var{bol}, and returns the position after the
comment-start token and any whitespace characters following that
token. Comment-start tokens are defined by regular expression
@code{treesit-comment-start}. This function assumes @var{parent} is
the comment node.
comment-start token. Comment-start tokens are defined by regular
expression @code{comment-start-skip}. This function assumes
@var{parent} is the comment node.
@end ftable
@end defvar

View file

@ -1735,20 +1735,6 @@ For more information of these built-in tree-sitter features,
For supporting mixing of multiple languages in a major mode,
@pxref{Multiple Languages}.
Setting the following local variables allows tree-sitter's indentation
engine to correctly indent multi-line comments:
@defvar treesit-comment-start
This should be a regular expression matching an opening comment token.
For example, it should match @samp{//}, @samp{////}, @samp{/*},
@samp{/****}, etc., in C.
@end defvar
@defvar treesit-comment-end
This should be a regular expression matching a closing comment token.
For example, it should match @samp{*/}, @samp{****/}, etc., in C.
@end defvar
@node Tree-sitter C API
@section Tree-sitter C API Correspondence

View file

@ -857,9 +857,10 @@ error is signaled.
@defmac setopt [symbol form]@dots{}
This is like @code{setq} (see above), but meant for user options.
This macro uses the Customize machinery to set the variable(s). In
particular, @code{setopt} will run the setter function associated with
the variable. For instance, if you have:
This macro uses the Customize machinery to set the variable(s)
(@pxref{Variable Definitions}). In particular, @code{setopt} will run
the setter function associated with the variable. For instance, if
you have:
@example
@group
@ -884,6 +885,12 @@ will also issue a message:
option. For instance, using @code{setopt} to set a user option
defined with a @code{number} type to a string will signal an error.
Unlike @code{defcustom} and related customization commands, such as
@code{customize-variable}, @code{setopt} is meant for non-interactive
use, in particular in the user init file. For that reason, it doesn't
record the standard, saved, and user-set values, and doesn't mark the
variable as candidate for saving in the custom file.
The @code{setopt} macro can be used on regular, non-user option
variables, but is much less efficient than @code{setq}. The main use
case for this macro is setting user options in the user's init file.

View file

@ -20021,11 +20021,16 @@ changes.
| =nofnadjust= | Do not renumber and sort automatically. |
#+vindex: org-hide-block-startup
To hide blocks on startup, use these keywords. The
corresponding variable is ~org-hide-block-startup~.
#+vindex: org-hide-drawer-startup
To hide blocks or drawers on startup, use these keywords. The
corresponding variables are ~org-hide-block-startup~ and
~org-hide-drawer-startup~.
| =hideblocks= | Hide all begin/end blocks on startup. |
| =nohideblocks= | Do not hide blocks on startup. |
| =hidedrawers= | Hide all begin/end blocks on startup. |
| =nohidedrawers= | Do not hide blocks on startup. |
| =hideblocks= | Hide all begin/end blocks on startup. |
| =nohideblocks= | Do not hide blocks on startup. |
#+vindex: org-pretty-entities
The display of entities as UTF-8 characters is governed by the

View file

@ -31,15 +31,43 @@ in the Emacs tree should be natively compiled ahead of time. (This is
slow on most machines.)
+++
** Emacs can be built with the 'tree-sitter' parsing library.
** Emacs can be built with the tree-sitter parsing library.
This library, together with grammar libraries, provides incremental
parsing capabilities for several popular programming languages and
other formatted files. Emacs built with this library offers major
modes, described elsewhere in this file, that are based on the
'tree-sitter's parsers. If you have the 'tree-sitter' library
tree-sitter's parsers. If you have the tree-sitter library
installed, the configure script will automatically include it in the
build; use '--without-tree-sitter' at configure time to disable that.
Emacs modes based on the tree-sitter library require an additional
grammar library for each mode. These grammar libraries provide the
tree-sitter library with language-specific lexical analysis and
parsing capabilities, and are developed separately from the
tree-sitter library itself. If you don't have a grammar library
required by some Emacs major mode, and your distro doesn't provide it
as an installable package, you can compile and install such a library
yourself. Many libraries can be downloaded from the tree-sitter site:
https://github.com/tree-sitter
To compile such a library, compile the files "scanner.c" and "parser.c"
(sometimes named "scanner.cc" and "parser.cc") in the "src" subdirectory
of the library's source tree using the C or C++ compiler, then link
these two files into a shared library named "libtree-sitter-LANG.so",
where LANG is the name of the language supported by the grammar as it
is expected by the Emacs major mode (for example, "c" for 'c-ts-mode',
"cpp" for 'c++-ts-mode', "python" for 'python-ts-mode', etc.). Then place
the shared library you've built in the same directory where you keep
the other shared libraries used by Emacs, or in the "tree-sitter"
subdirectory of your 'user-emacs-directory', or in a directory
mentioned in the variable 'treesit-extra-load-path'.
You only need to install language grammar libraries required by the
Emacs modes you will use, as Emacs loads these libraries only when the
corresponding mode is turned on in some buffer for the first time in
an Emacs session.
+++
** Emacs can be built with built-in support for accessing SQLite databases.
This uses the popular sqlite3 library, and can be disabled by using
@ -2725,6 +2753,9 @@ project-dedicated or global) is specified by the new
---
*** New user option 'ruby-toggle-block-space-before-parameters'.
---
*** Support for endless methods.
** Eshell
+++
@ -3023,6 +3054,29 @@ indentation, and navigation by defuns based on parsing the buffer text
by a tree-sitter parser. Some major modes also offer support for
Imenu and 'which-func'.
Where major modes already exist in Emacs for editing certain kinds of
files, the new modes based on tree-sitter are for now entirely
optional, and you must turn them on manually, or customize
'auto-mode-alist' to turn them on automatically.
Each major mode based on tree-sitter needs a language grammar library,
usually named "libtree-sitter-LANG.so" ("libtree-sitter-LANG.dll" on
MS-Windows), where LANG is the corresponding language name. Emacs
looks for these libraries in the following places:
. in the directories mentioned in the list 'treesit-extra-load-path'
. in the "tree-sitter" subdirectory of your 'user-emacs-directory'
(by default, "~/.emacs.d/tree-sitter")
. in the standard system directories where other shared libraries are
usually installed
We recommend to install these libraries in one of the standard system
locations (the last place in the above list).
If a language grammar library required by a mode is not found in any
of the above places, the mode will signal an error when you try to
turn it on.
*** New major mode 'typescript-ts-mode'.
A major mode based on the tree-sitter library for editing programs
in the TypeScript language. This mode is auto-enabled for files with
@ -3089,6 +3143,10 @@ the Go language. It is auto-enabled for files with the ".go" extension.
A major mode based on the tree-sitter library for editing "go.mod"
files. It is auto-enabled for files which are named "go.mod".
*** New major mode 'rust-ts-mode'.
A major mode based on the tree-sitter library for editing programs in
the Rust language. It is auto-enabled for files with the ".rs" extension.
* Incompatible Lisp Changes in Emacs 29.1
@ -4472,6 +4530,15 @@ set is too big to transfer to Emacs every time a completion is
needed. The table uses new 'external' completion style exclusively
and cannot work with regular styles such as 'basic' or 'flex'.
+++
** Magic file name handlers for 'make-directory-internal' are no longer needed.
Instead, Emacs uses the already-existing 'make-directory' handlers.
+++
** '(make-directory DIR t)' returns non-nil if DIR already exists.
This can let a caller know whether it created DIR. Formerly,
'make-directory's return value was unspecified.
* Changes in Emacs 29.1 on Non-Free Operating Systems

View file

@ -1073,7 +1073,7 @@ plain variables. This means that `setopt' will execute any
;; Check that the type is correct.
(when-let ((type (get variable 'custom-type)))
(unless (widget-apply (widget-convert type) :match value)
(user-error "Value `%S' does not match type %s" value type)))
(warn "Value `%S' does not match type %s" value type)))
(put variable 'custom-check-value (list value))
(funcall (or (get variable 'custom-set) #'set-default) variable value))

View file

@ -4005,8 +4005,11 @@ display a message."
:command (list
(expand-file-name invocation-name
invocation-directory)
"-no-comp-spawn" "--batch" "-l"
temp-file)
"-no-comp-spawn" "--batch"
"--eval"
;; Suppress Abort dialogs on MS-Windows
"(setq w32-disable-abort-dialog t)"
"-l" temp-file)
:sentinel
(lambda (process _event)
(run-hook-with-args

View file

@ -6193,18 +6193,17 @@ instance of such commands."
(rename-buffer (generate-new-buffer-name base-name))
(force-mode-line-update))))
(defun files--ensure-directory (dir)
"Make directory DIR if it is not already a directory. Return nil."
(defun files--ensure-directory (mkdir dir)
"Use function MKDIR to make directory DIR if it is not already a directory.
Return non-nil if DIR is already a directory."
(condition-case err
(make-directory-internal dir)
(funcall mkdir dir)
(error
(unless (file-directory-p dir)
(signal (car err) (cdr err))))))
(or (file-directory-p dir)
(signal (car err) (cdr err))))))
(defun make-directory (dir &optional parents)
"Create the directory DIR and optionally any nonexistent parent dirs.
If DIR already exists as a directory, signal an error, unless
PARENTS is non-nil.
Interactively, the default choice of directory to create is the
current buffer's default directory. That is useful when you have
@ -6214,8 +6213,9 @@ Noninteractively, the second (optional) argument PARENTS, if
non-nil, says whether to create parent directories that don't
exist. Interactively, this happens by default.
If creating the directory or directories fail, an error will be
raised."
Return non-nil if PARENTS is non-nil and DIR already exists as a
directory, and nil if DIR did not already exist but was created.
Signal an error if unsuccessful."
(interactive
(list (read-file-name "Make directory: " default-directory default-directory
nil nil)
@ -6223,25 +6223,32 @@ raised."
;; If default-directory is a remote directory,
;; make sure we find its make-directory handler.
(setq dir (expand-file-name dir))
(let ((handler (find-file-name-handler dir 'make-directory)))
(if handler
(funcall handler 'make-directory dir parents)
(if (not parents)
(make-directory-internal dir)
(let ((dir (directory-file-name (expand-file-name dir)))
create-list parent)
(while (progn
(setq parent (directory-file-name
(file-name-directory dir)))
(condition-case ()
(files--ensure-directory dir)
(file-missing
;; Do not loop if root does not exist (Bug#2309).
(not (string= dir parent)))))
(setq create-list (cons dir create-list)
dir parent))
(dolist (dir create-list)
(files--ensure-directory dir)))))))
(let ((mkdir (if-let ((handler (find-file-name-handler dir 'make-directory)))
#'(lambda (dir)
;; Use 'ignore' since the handler might be designed for
;; Emacs 28-, so it might return an (undocumented)
;; non-nil value, whereas the Emacs 29+ convention is
;; to return nil here.
(ignore (funcall handler 'make-directory dir)))
#'make-directory-internal)))
(if (not parents)
(funcall mkdir dir)
(let ((dir (directory-file-name (expand-file-name dir)))
already-dir create-list parent)
(while (progn
(setq parent (directory-file-name
(file-name-directory dir)))
(condition-case ()
(ignore (setq already-dir
(files--ensure-directory mkdir dir)))
(error
;; Do not loop if root does not exist (Bug#2309).
(not (string= dir parent)))))
(setq create-list (cons dir create-list)
dir parent))
(dolist (dir create-list)
(setq already-dir (files--ensure-directory mkdir dir)))
already-dir))))
(defun make-empty-file (filename &optional parents)
"Create an empty file FILENAME.
@ -6435,7 +6442,7 @@ into NEWNAME instead."
;; copy-directory handler.
(let ((handler (or (find-file-name-handler directory 'copy-directory)
(find-file-name-handler newname 'copy-directory)))
(follow parents))
follow)
(if handler
(funcall handler 'copy-directory directory
newname keep-time parents copy-contents)
@ -6455,19 +6462,24 @@ into NEWNAME instead."
t)
(make-symbolic-link target newname t)))
;; Else proceed to copy as a regular directory
(cond ((not (directory-name-p newname))
;; first by creating the destination directory if needed,
;; preparing to follow any symlink to a directory we did not create.
(setq follow
(if (not (directory-name-p newname))
;; If NEWNAME is not a directory name, create it;
;; that is where we will copy the files of DIRECTORY.
(make-directory newname parents))
(make-directory newname parents)
;; NEWNAME is a directory name. If COPY-CONTENTS is non-nil,
;; create NEWNAME if it is not already a directory;
;; otherwise, create NEWNAME/[DIRECTORY-BASENAME].
((if copy-contents
(or parents (not (file-directory-p newname)))
(unless copy-contents
(setq newname (concat newname
(file-name-nondirectory directory))))
(make-directory (directory-file-name newname) parents))
(t (setq follow t)))
(condition-case err
(make-directory (directory-file-name newname) parents)
(error
(or (file-directory-p newname)
(signal (car err) (cdr err)))))))
;; Copy recursively.
(dolist (file
@ -8467,14 +8479,6 @@ If the value is nil, Emacs uses a freedesktop.org-style trashcan."
(declare-function system-move-file-to-trash "w32fns.c" (filename))
(defun file-exists-in-trash-p (filename)
"Return non-nil if FILENAME exists in the trash.
This is like `file-exists-p', but it also returns non-nil
if FILENAME is a dangling symlink, to allow trashing such files."
(or (file-exists-p filename)
(file-symlink-p filename)))
(defun move-file-to-trash (filename)
"Move the file (or directory) named FILENAME to the trash.
When `delete-by-moving-to-trash' is non-nil, this function is
@ -8505,7 +8509,7 @@ Otherwise, trash FILENAME using the freedesktop.org conventions,
(unless (file-directory-p trash-dir)
(make-directory trash-dir t))
;; Ensure that the trashed file-name is unique.
(if (file-exists-in-trash-p new-fn)
(if (file-attributes new-fn)
(let ((version-control t)
(backup-directory-alist nil))
(setq new-fn (car (find-backup-file-name new-fn)))))
@ -8582,7 +8586,7 @@ Otherwise, trash FILENAME using the freedesktop.org conventions,
;; We're checking further down whether the info file
;; exists, but the file name may exist in the trash
;; directory even if there is no info file for it.
(when (file-exists-in-trash-p
(when (file-attributes
(file-name-concat trash-files-dir files-base))
(setq overwrite t
files-base (file-name-nondirectory
@ -8620,7 +8624,7 @@ Otherwise, trash FILENAME using the freedesktop.org conventions,
(let ((delete-by-moving-to-trash nil)
(new-fn (file-name-concat trash-files-dir files-base)))
(if (or (not is-directory)
(not (file-exists-in-trash-p new-fn)))
(not (file-attributes new-fn)))
(rename-file fn new-fn overwrite)
(copy-directory fn
(file-name-as-directory new-fn)

View file

@ -296,7 +296,7 @@ This variable is set by `nnmaildir-request-article'.")
(if (file-attributes file) (delete-file file))))
(defun nnmaildir--mkdir (dir)
(or (file-exists-p (file-name-as-directory dir))
(make-directory-internal (directory-file-name dir))))
(make-directory (directory-file-name dir))))
(defun nnmaildir--mkfile (file)
(write-region "" nil file nil 'no-message))
(defun nnmaildir--delete-dir-files (dir ls)

View file

@ -2435,7 +2435,7 @@ If cursor is not at the end of the user input, move to end of input."
filename))
(ido-record-command method dirname)
(ido-record-work-directory dirname)
(make-directory-internal dirname)
(make-directory dirname)
(funcall method dirname))
(t
;; put make-directory command on history

View file

@ -80,9 +80,14 @@ commands consecutively. Filled by
(defvar rmail-summary-message-parents-vector nil
"Vector that holds a list of indices of parents for each message.
Message A is parent to message B if the id of A appear in the
References or In-reply-to fields of B, or if A is the first
message with the same subject as B. First element is ignored.")
Message A is parent of message B if the id of A appears in the
\"References\" or \"In-reply-to\" fields of B, or if A is the first
message with the same \"Subject\" as B. First element is ignored.")
(defvar rmail-summary-message-descendants-vector nil
"Vector that holds the direct descendants of each message.
This is the antipode of `rmail-summary-message-parents-vector'.
First element is ignored.")
(defvar rmail-summary-font-lock-keywords
'(("^ *[0-9]+D.*" . font-lock-string-face) ; Deleted.
@ -318,11 +323,13 @@ message with the same subject as B. First element is ignored.")
(defun rmail-summary-fill-message-ids-hash-table ()
"Fill `rmail-summary-message-ids-hash-table'."
(with-current-buffer rmail-buffer
(setq rmail-summary-message-ids-hash-table (make-hash-table :test 'equal :size 1024))
(setq rmail-summary-message-ids-hash-table
(make-hash-table :test 'equal :size 1024))
(let ((msgnum 1))
(while (<= msgnum rmail-total-messages)
(let ((id (rmail-get-header "Message-ID" msgnum)))
(puthash id (cons (cons id msgnum) (gethash id rmail-summary-message-ids-hash-table))
(puthash id (cons (cons id msgnum)
(gethash id rmail-summary-message-ids-hash-table))
rmail-summary-message-ids-hash-table))
(setq msgnum (1+ msgnum))))))
@ -331,14 +338,18 @@ message with the same subject as B. First element is ignored.")
(if header
(split-string header "[ \f\t\n\r\v,;]+"))))
(defun rmail-summary-fill-message-parents-vector ()
"Fill `rmail-summary-message-parents-vector'."
(defun rmail-summary-fill-message-parents-and-descs-vectors ()
"Fill parents and descendats vectors for messages.
This populates `rmail-summary-message-parents-vector'
and `rmail-summary-message-descendants-vector'."
(with-current-buffer rmail-buffer
(rmail-summary-fill-message-ids-hash-table)
(setq rmail-summary-subjects-hash-table
(make-hash-table :test 'equal :size 1024))
(setq rmail-summary-message-parents-vector
(make-vector (1+ rmail-total-messages) nil))
(setq rmail-summary-message-descendants-vector
(make-vector (1+ rmail-total-messages) nil))
(let ((msgnum 1))
(while (<= msgnum rmail-total-messages)
(let* ((parents nil)
@ -346,18 +357,27 @@ message with the same subject as B. First element is ignored.")
(subj-cell (gethash subject rmail-summary-subjects-hash-table))
(subj-par (assoc subject subj-cell))
(refs (rmail-summary--split-header-field "References" msgnum))
(reply-to (rmail-summary--split-header-field "In-reply-to"
(reply-tos (rmail-summary--split-header-field "In-reply-to"
msgnum)))
(if subj-par
(setq parents (cons (cdr subj-par) parents))
(progn
(setq parents (cons (cdr subj-par) nil))
(aset rmail-summary-message-descendants-vector (cdr subj-par)
(cons msgnum
(aref rmail-summary-message-descendants-vector
(cdr subj-par)))))
(puthash subject (cons (cons subject msgnum) subj-cell)
rmail-summary-subjects-hash-table))
(dolist (id (append refs reply-to))
(dolist (id (append refs reply-tos))
(let ((ent
(assoc id
(gethash id rmail-summary-message-ids-hash-table))))
(if ent
(setq parents (cons (cdr ent) parents)))))
(when ent
(setq parents (cons (cdr ent) parents))
(aset rmail-summary-message-descendants-vector (cdr ent)
(cons msgnum
(aref rmail-summary-message-descendants-vector
(cdr ent)))))))
(aset rmail-summary-message-parents-vector msgnum parents)
(setq msgnum (1+ msgnum)))))))
@ -387,20 +407,6 @@ the messages that are displayed."
(interactive)
(rmail-new-summary "All" '(rmail-summary) nil))
(defun rmail-summary-direct-descendants (msgnum encountered-msgs)
"Find all direct descendants of MSGNUM, ignoring ENCOUNTERED-MSGS.
Assumes `rmail-summary-message-parents-vector' is filled. Ignores messages
already ticked in ENCOUNTERED-MSGS."
(let (desc
(msg 1))
(while (<= msg rmail-total-messages)
(when (and
(not (aref encountered-msgs msg))
(memq msgnum (aref rmail-summary-message-parents-vector msg)))
(setq desc (cons msg desc)))
(setq msg (1+ msg)))
desc))
(defun rmail-summary--walk-thread-message-recursively (msgnum encountered-msgs)
"Add parents and descendants of message MSGNUM to ENCOUNTERED-MSGS, recursively."
(unless (aref encountered-msgs msgnum)
@ -412,7 +418,7 @@ already ticked in ENCOUNTERED-MSGS."
(mapc walk-thread-msg
(aref rmail-summary-message-parents-vector msgnum))
(mapc walk-thread-msg
(rmail-summary-direct-descendants msgnum encountered-msgs)))))
(aref rmail-summary-message-descendants-vector msgnum)))))
;;;###autoload
(defun rmail-summary-by-thread (&optional msgnum)
@ -430,7 +436,7 @@ headers of the messages."
(unless (and rmail-summary-message-parents-vector
(= (length rmail-summary-message-parents-vector)
(1+ rmail-total-messages)))
(rmail-summary-fill-message-parents-vector))
(rmail-summary-fill-message-parents-and-descs-vectors))
(let ((enc-msgs (make-bool-vector (1+ rmail-total-messages) nil)))
(rmail-summary--walk-thread-message-recursively msgnum enc-msgs)
(rmail-new-summary (format "thread containing message %d" msgnum)

View file

@ -215,11 +215,18 @@ It must be supported by libarchive(3).")
;; In older Emacs (prior 27.1), `tramp-archive-autoload-file-name-regexp'
;; is not autoloaded. So we cannot expect it to be known in
;; tramp-loaddefs.el. But it exists, when tramp-archive.el is loaded.
;; We must wrap it into `eval-when-compile'. Otherwise, there could
;; be an "Eager macro-expansion failure" when unloading/reloading Tramp.
;;;###tramp-autoload
(defconst tramp-archive-file-name-regexp
(ignore-errors (tramp-archive-autoload-file-name-regexp))
(eval-when-compile (ignore-errors (tramp-archive-autoload-file-name-regexp)))
"Regular expression matching archive file names.")
;; The value above is nil for Emacs 26. Set it now.
(if (<= emacs-major-version 26)
(setq tramp-archive-file-name-regexp
(ignore-errors (tramp-archive-autoload-file-name-regexp))))
;;;###tramp-autoload
(defconst tramp-archive-method "archive"
"Method name for archives in GVFS.")

View file

@ -279,7 +279,7 @@ See `tramp-actions-before-shell' for more info.")
(lock-file . tramp-handle-lock-file)
(make-auto-save-file-name . tramp-handle-make-auto-save-file-name)
(make-directory . tramp-smb-handle-make-directory)
(make-directory-internal . tramp-smb-handle-make-directory-internal)
(make-directory-internal . ignore)
(make-lock-file-name . tramp-handle-make-lock-file-name)
(make-nearby-temp-file . tramp-handle-make-nearby-temp-file)
(make-process . ignore)
@ -1186,12 +1186,21 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored."
(make-directory ldir parents))
;; Just do it.
(when (file-directory-p ldir)
(make-directory-internal dir))
(tramp-smb-send-command
v (if (tramp-smb-get-cifs-capabilities v)
(format "posix_mkdir %s %o"
(tramp-smb-shell-quote-localname v) (default-file-modes))
(format "mkdir %s" (tramp-smb-shell-quote-localname v))))
;; We must also flush the cache of the directory, because
;; `file-attributes' reads the values from there.
(tramp-flush-file-properties v localname))
(unless (file-directory-p dir)
(tramp-error v 'file-error "Couldn't make directory %s" dir)))))
;; This is not used anymore.
(defun tramp-smb-handle-make-directory-internal (directory)
"Like `make-directory-internal' for Tramp files."
(declare (obsolete nil "29.1"))
(setq directory (directory-file-name (expand-file-name directory)))
(unless (file-name-absolute-p directory)
(setq directory (expand-file-name directory default-directory)))

View file

@ -2605,12 +2605,14 @@ Must be handled by the callers."
file-selinux-context file-symlink-p file-truename
file-writable-p find-backup-file-name get-file-buffer
insert-directory insert-file-contents load
make-directory make-directory-internal set-file-acl
set-file-modes set-file-selinux-context set-file-times
make-directory set-file-acl set-file-modes
set-file-selinux-context set-file-times
substitute-in-file-name unhandled-file-name-directory
vc-registered
;; Emacs 27+ only.
file-system-info
;; Emacs 28- only.
make-directory-internal
;; Emacs 28+ only.
file-locked-p lock-file make-lock-file-name unlock-file
;; Emacs 29+ only.

View file

@ -241,11 +241,11 @@ This function is called by `org-babel-execute-src-block'."
(defun org-babel-R-assign-elisp (name value colnames-p rownames-p)
"Construct R code assigning the elisp VALUE to a variable named NAME."
(if (listp value)
(let* ((lengths (mapcar 'length (cl-remove-if-not 'sequencep value)))
(let* ((lengths (mapcar 'length (cl-remove-if-not 'listp value)))
(max (if lengths (apply 'max lengths) 0))
(min (if lengths (apply 'min lengths) 0)))
;; Ensure VALUE has an orgtbl structure (depth of at least 2).
(unless (listp (car value)) (setq value (list value)))
(unless (listp (car value)) (setq value (mapcar 'list value)))
(let ((file (orgtbl-to-tsv value '(:fmt org-babel-R-quote-tsv-field)))
(header (if (or (eq (nth 1 value) 'hline) colnames-p)
"TRUE" "FALSE"))

View file

@ -91,7 +91,7 @@ end")
(list
"set (0, \"defaultfigurevisible\", \"off\");"
full-body
(format "print -dpng %s" gfx-file))
(format "print -dpng %S\nans=%S" gfx-file gfx-file))
"\n")
full-body)
result-type matlabp)))

View file

@ -5295,6 +5295,7 @@ indentation removed from its contents."
;; `org-element--cache-diagnostics-ring-size', `org-element--cache-map-statistics',
;; `org-element--cache-map-statistics-threshold'.
;;;###autoload
(defvar org-element-use-cache t
"Non-nil when Org parser should cache its results.")

View file

@ -222,8 +222,11 @@ function will be called with a single argument - collection."
(defconst org-persist-index-file "index"
"File name used to store the data index.")
(defvar org-persist-disable-when-emacs-Q t
"Disable persistence when Emacs is called with -Q command line arg.")
(defvar org-persist--disable-when-emacs-Q t
"Disable persistence when Emacs is called with -Q command line arg.
When non-nil, this sets `org-persist-directory' to temporary directory.
This variable must be set before loading org-persist library.")
(defvar org-persist-before-write-hook nil
"Abnormal hook ran before saving data.
@ -662,12 +665,13 @@ COLLECTION is the plist holding data collection."
(file-copy (org-file-name-concat
org-persist-directory
(format "%s-%s.%s" persist-file (md5 path) ext))))
(unless (file-exists-p (file-name-directory file-copy))
(make-directory (file-name-directory file-copy) t))
(if (org--should-fetch-remote-resource-p path)
(url-copy-file path file-copy 'overwrite)
(error "The remote resource %S is considered unsafe, and will not be downloaded."
path))
(unless (file-exists-p file-copy)
(unless (file-exists-p (file-name-directory file-copy))
(make-directory (file-name-directory file-copy) t))
(if (org--should-fetch-remote-resource-p path)
(url-copy-file path file-copy 'overwrite)
(error "The remote resource %S is considered unsafe, and will not be downloaded."
path)))
(format "%s-%s.%s" persist-file (md5 path) ext)))))
(defun org-persist-write:index (container _)
@ -771,43 +775,39 @@ ASSOCIATED can be a plist, a buffer, or a string.
A buffer is treated as (:buffer ASSOCIATED).
A string is treated as (:file ASSOCIATED).
When LOAD? is non-nil, load the data instead of reading."
(unless org-persist--index (org-persist--load-index))
(setq associated (org-persist--normalize-associated associated))
(setq container (org-persist--normalize-container container))
(unless (and org-persist-disable-when-emacs-Q
;; FIXME: This is relying on undocumented fact that
;; Emacs sets `user-init-file' to nil when loaded with
;; "-Q" argument.
(not user-init-file))
(let* ((collection (org-persist--find-index `(:container ,container :associated ,associated)))
(persist-file
(when collection
(org-file-name-concat
org-persist-directory
(plist-get collection :persist-file))))
(data nil))
(when (and collection
(file-exists-p persist-file)
(or (not (plist-get collection :expiry)) ; current session
(not (org-persist--gc-expired-p
(plist-get collection :expiry) collection)))
(or (not hash-must-match)
(and (plist-get associated :hash)
(equal (plist-get associated :hash)
(plist-get (plist-get collection :associated) :hash)))))
(unless (seq-find (lambda (v)
(run-hook-with-args-until-success 'org-persist-before-read-hook v associated))
(plist-get collection :container))
(setq data (or (gethash persist-file org-persist--write-cache)
(org-persist--read-elisp-file persist-file)))
(when data
(cl-loop for container in (plist-get collection :container)
with result = nil
do
(if load?
(push (org-persist-load:generic container (alist-get container data nil nil #'equal) collection) result)
(push (org-persist-read:generic container (alist-get container data nil nil #'equal) collection) result))
(run-hook-with-args 'org-persist-after-read-hook container associated)
finally return (if (= 1 (length result)) (car result) result))))))))
(let* ((collection (org-persist--find-index `(:container ,container :associated ,associated)))
(persist-file
(when collection
(org-file-name-concat
org-persist-directory
(plist-get collection :persist-file))))
(data nil))
(when (and collection
(file-exists-p persist-file)
(or (not (plist-get collection :expiry)) ; current session
(not (org-persist--gc-expired-p
(plist-get collection :expiry) collection)))
(or (not hash-must-match)
(and (plist-get associated :hash)
(equal (plist-get associated :hash)
(plist-get (plist-get collection :associated) :hash)))))
(unless (seq-find (lambda (v)
(run-hook-with-args-until-success 'org-persist-before-read-hook v associated))
(plist-get collection :container))
(setq data (or (gethash persist-file org-persist--write-cache)
(org-persist--read-elisp-file persist-file)))
(when data
(cl-loop for container in (plist-get collection :container)
with result = nil
do
(if load?
(push (org-persist-load:generic container (alist-get container data nil nil #'equal) collection) result)
(push (org-persist-read:generic container (alist-get container data nil nil #'equal) collection) result))
(run-hook-with-args 'org-persist-after-read-hook container associated)
finally return (if (= 1 (length result)) (car result) result)))))))
(defun org-persist-load (container &optional associated hash-must-match)
"Load CONTAINER data for ASSOCIATED.
@ -843,62 +843,66 @@ The return value is nil when writing fails and the written value (as
returned by `org-persist-read') on success.
When IGNORE-RETURN is non-nil, just return t on success without calling
`org-persist-read'."
(unless (and org-persist-disable-when-emacs-Q
;; FIXME: This is relying on undocumented fact that
;; Emacs sets `user-init-file' to nil when loaded with
;; "-Q" argument.
(not user-init-file))
(setq associated (org-persist--normalize-associated associated))
;; Update hash
(when (and (plist-get associated :file)
(plist-get associated :hash)
(get-file-buffer (plist-get associated :file)))
(setq associated (org-persist--normalize-associated (get-file-buffer (plist-get associated :file)))))
(let ((collection (org-persist--get-collection container associated)))
(setf collection (plist-put collection :associated associated))
(unless (or
;; Prevent data leakage from encrypted files.
;; We do it in somewhat paranoid manner and do not
;; allow anything related to encrypted files to be
;; written.
(and (plist-get associated :file)
(string-match-p epa-file-name-regexp (plist-get associated :file)))
(seq-find (lambda (v)
(run-hook-with-args-until-success 'org-persist-before-write-hook v associated))
(plist-get collection :container)))
(when (or (file-exists-p org-persist-directory) (org-persist--save-index))
(let ((file (org-file-name-concat org-persist-directory (plist-get collection :persist-file)))
(data (mapcar (lambda (c) (cons c (org-persist-write:generic c collection)))
(plist-get collection :container))))
(puthash file data org-persist--write-cache)
(org-persist--write-elisp-file file data)
(or ignore-return (org-persist-read container associated))))))))
(setq associated (org-persist--normalize-associated associated))
;; Update hash
(when (and (plist-get associated :file)
(plist-get associated :hash)
(get-file-buffer (plist-get associated :file)))
(setq associated (org-persist--normalize-associated (get-file-buffer (plist-get associated :file)))))
(let ((collection (org-persist--get-collection container associated)))
(setf collection (plist-put collection :associated associated))
(unless (or
;; Prevent data leakage from encrypted files.
;; We do it in somewhat paranoid manner and do not
;; allow anything related to encrypted files to be
;; written.
(and (plist-get associated :file)
(string-match-p epa-file-name-regexp (plist-get associated :file)))
(seq-find (lambda (v)
(run-hook-with-args-until-success 'org-persist-before-write-hook v associated))
(plist-get collection :container)))
(when (or (file-exists-p org-persist-directory) (org-persist--save-index))
(let ((file (org-file-name-concat org-persist-directory (plist-get collection :persist-file)))
(data (mapcar (lambda (c) (cons c (org-persist-write:generic c collection)))
(plist-get collection :container))))
(puthash file data org-persist--write-cache)
(org-persist--write-elisp-file file data)
(or ignore-return (org-persist-read container associated)))))))
(defun org-persist-write-all (&optional associated)
"Save all the persistent data.
When ASSOCIATED is non-nil, only save the matching data."
(unless org-persist--index (org-persist--load-index))
(setq associated (org-persist--normalize-associated associated))
(let (all-containers)
(dolist (collection org-persist--index)
(if associated
(when collection
(cl-pushnew (plist-get collection :container) all-containers :test #'equal))
(condition-case err
(org-persist-write (plist-get collection :container) (plist-get collection :associated) t)
(error
(message "%s. Deleting bad index entry." err)
(org-persist--remove-from-index collection)
nil))))
(dolist (container all-containers)
(let ((collection (org-persist--find-index `(:container ,container :associated ,associated))))
(when collection
(unless
(and (equal 1 (length org-persist--index))
;; The single collection only contains a single container
;; in the container list.
(equal 1 (length (plist-get (car org-persist--index) :container)))
;; The container is an `index' container.
(eq 'index (caar (plist-get (car org-persist--index) :container)))
;; No `org-persist-directory' exists yet.
(not (file-exists-p org-persist-directory)))
(let (all-containers)
(dolist (collection org-persist--index)
(if associated
(when collection
(cl-pushnew (plist-get collection :container) all-containers :test #'equal))
(condition-case err
(org-persist-write container associated t)
(org-persist-write (plist-get collection :container) (plist-get collection :associated) t)
(error
(message "%s. Deleting bad index entry." err)
(org-persist--remove-from-index collection)
nil)))))))
nil))))
(dolist (container all-containers)
(let ((collection (org-persist--find-index `(:container ,container :associated ,associated))))
(when collection
(condition-case err
(org-persist-write container associated t)
(error
(message "%s. Deleting bad index entry." err)
(org-persist--remove-from-index collection)
nil))))))))
(defun org-persist-write-all-buffer ()
"Call `org-persist-write-all' in current buffer.
@ -931,45 +935,40 @@ Do nothing in an indirect buffer."
(defun org-persist-gc ()
"Remove expired or unregistered containers.
Also, remove containers associated with non-existing files."
(unless (and org-persist-disable-when-emacs-Q
;; FIXME: This is relying on undocumented fact that
;; Emacs sets `user-init-file' to nil when loaded with
;; "-Q" argument.
(not user-init-file))
(let (new-index (remote-files-num 0))
(dolist (collection org-persist--index)
(let* ((file (plist-get (plist-get collection :associated) :file))
(file-remote (when file (file-remote-p file)))
(persist-file (when (plist-get collection :persist-file)
(org-file-name-concat
org-persist-directory
(plist-get collection :persist-file))))
(expired? (org-persist--gc-expired-p
(plist-get collection :expiry) collection)))
(when persist-file
(when file
(when file-remote (cl-incf remote-files-num))
(unless (if (not file-remote)
(file-exists-p file)
(pcase org-persist-remote-files
('t t)
('check-existence
(file-exists-p file))
((pred numberp)
(<= org-persist-remote-files remote-files-num))
(_ nil)))
(setq expired? t)))
(if expired?
(org-persist--gc-persist-file persist-file)
(push collection new-index)))))
(setq org-persist--index (nreverse new-index)))))
(let (new-index (remote-files-num 0))
(dolist (collection org-persist--index)
(let* ((file (plist-get (plist-get collection :associated) :file))
(file-remote (when file (file-remote-p file)))
(persist-file (when (plist-get collection :persist-file)
(org-file-name-concat
org-persist-directory
(plist-get collection :persist-file))))
(expired? (org-persist--gc-expired-p
(plist-get collection :expiry) collection)))
(when persist-file
(when file
(when file-remote (cl-incf remote-files-num))
(unless (if (not file-remote)
(file-exists-p file)
(pcase org-persist-remote-files
('t t)
('check-existence
(file-exists-p file))
((pred numberp)
(<= org-persist-remote-files remote-files-num))
(_ nil)))
(setq expired? t)))
(if expired?
(org-persist--gc-persist-file persist-file)
(push collection new-index)))))
(setq org-persist--index (nreverse new-index))))
;; Automatically write the data, but only when we have write access.
(let ((dir (directory-file-name
(file-name-as-directory org-persist-directory))))
(while (and (not (file-exists-p dir))
(not (equal dir (setq dir (directory-file-name
(file-name-directory dir)))))))
(file-name-directory dir)))))))
(if (not (file-writable-p dir))
(message "Missing write access rights to org-persist-directory: %S"
org-persist-directory)
@ -978,6 +977,15 @@ Also, remove containers associated with non-existing files."
;; So we are adding the hook after `org-persist-write-all'.
(add-hook 'kill-emacs-hook #'org-persist-gc)))
;; Point to temp directory when `org-persist--disable-when-emacs-Q' is set.
(if (and org-persist--disable-when-emacs-Q
;; FIXME: This is relying on undocumented fact that
;; Emacs sets `user-init-file' to nil when loaded with
;; "-Q" argument.
(not user-init-file))
(setq org-persist-directory
(make-temp-file "org-persist-" 'dir)))
(add-hook 'after-init-hook #'org-persist-load-all)
(provide 'org-persist)

View file

@ -11,7 +11,7 @@ Inserted by installing Org mode or when a release is made."
(defun org-git-version ()
"The Git version of Org mode.
Inserted by installing Org or when a release is made."
(let ((org-git-version "release_9.6-31-g954a95"))
(let ((org-git-version "release_9.6-49-g47d129"))
org-git-version))
(provide 'org-version)

View file

@ -102,6 +102,7 @@
(require 'org-cycle)
(defvaralias 'org-hide-block-startup 'org-cycle-hide-block-startup)
(defvaralias 'org-hide-drawer-startup 'org-cycle-hide-drawer-startup)
(defvaralias 'org-pre-cycle-hook 'org-cycle-pre-hook)
(defvaralias 'org-tab-first-hook 'org-cycle-tab-first-hook)
(defalias 'org-global-cycle #'org-cycle-global)
@ -4596,8 +4597,8 @@ is available. This option applies only if FILE is a URL."
This checks every pattern in `org-safe-remote-resources', and
returns non-nil if any of them match."
(let ((uri-patterns org-safe-remote-resources)
(file-uri (and buffer-file-name
(concat "file://" (file-truename buffer-file-name))))
(file-uri (and (buffer-file-name (buffer-base-buffer))
(concat "file://" (file-truename (buffer-file-name (buffer-base-buffer))))))
match-p)
(while (and (not match-p) uri-patterns)
(setq match-p (or (string-match-p (car uri-patterns) uri)
@ -4608,7 +4609,8 @@ returns non-nil if any of them match."
(defun org--confirm-resource-safe (uri)
"Ask the user if URI should be considered safe, returning non-nil if so."
(unless noninteractive
(let ((current-file (and buffer-file-name (file-truename buffer-file-name)))
(let ((current-file (and (buffer-file-name (buffer-base-buffer))
(file-truename (buffer-file-name (buffer-base-buffer)))))
(domain (and (string-match
(rx (seq "http" (? "s") "://")
(optional (+ (not (any "@/\n"))) "@")
@ -16399,6 +16401,10 @@ buffer boundaries with possible narrowing."
"Remove inline-display overlay if a corresponding region is modified."
(when (and ov after)
(delete ov org-inline-image-overlays)
;; Clear image from cache to avoid image not updating upon
;; changing on disk. See Emacs bug#59902.
(when (overlay-get ov 'org-image-overlay)
(image-flush (overlay-get ov 'display)))
(delete-overlay ov)))
(defun org-remove-inline-images (&optional beg end)

View file

@ -4612,12 +4612,17 @@ If LINK refers to a remote resource, modify it to point to a local
downloaded copy. Otherwise, return unchanged LINK."
(when (org-export-link-remote-p link)
(let* ((local-path (org-export-link--remote-local-copy link)))
(setcdr link
(thread-first (cadr link)
(plist-put :type "file")
(plist-put :path local-path)
(plist-put :raw-link (concat "file:" local-path))
list))))
(if local-path
(setcdr link
(thread-first (cadr link)
(plist-put :type "file")
(plist-put :path local-path)
(plist-put :raw-link (concat "file:" local-path))
list))
(display-warning
'(org export)
(format "unable to obtain local copy of %s"
(org-element-property :raw-link link))))))
link)
;;;; For References
@ -4753,23 +4758,27 @@ objects of the same type."
(let ((counter 0))
;; Increment counter until ELEMENT is found again.
(org-element-map (plist-get info :parse-tree)
(or types (org-element-type element))
(or (and types (cons (org-element-type element) types))
(org-element-type element))
(lambda (el)
(let ((cached (org-element-property :org-export--counter el)))
(cond
((eq element el) (1+ counter))
;; Use cached result.
((and cached (equal predicate (car cached)))
(cdr cached))
((and cached
(equal predicate (car cached))
(equal types (cadr cached)))
(setq counter (nth 2 cached))
nil)
((not predicate)
(cl-incf counter)
(org-element-put-property
el :org-export--counter (cons predicate counter))
el :org-export--counter (list predicate types counter))
nil)
((funcall predicate el info)
(cl-incf counter)
(org-element-put-property
el :org-export--counter (cons predicate counter))
el :org-export--counter (list predicate types counter))
nil))))
info 'first-match)))))

View file

@ -103,7 +103,6 @@ MODE is either `c' or `cpp'."
((node-is "case") parent-bol 0)
((node-is "preproc_arg") no-indent)
((and (parent-is "comment") comment-end) comment-start -1)
((parent-is "comment") comment-start-skip 0)
((node-is "labeled_statement") parent-bol 0)
((parent-is "labeled_statement") parent-bol c-ts-mode-indent-offset)
((match "preproc_ifdef" "compound_statement") point-min 0)

View file

@ -182,7 +182,7 @@ chosen (interactively or automatically)."
when probe return (cons probe args)
finally (funcall err)))))))
(defvar eglot-server-programs `((rust-mode . ,(eglot-alternatives '("rust-analyzer" "rls")))
(defvar eglot-server-programs `(((rust-ts-mode rust-mode) . ,(eglot-alternatives '("rust-analyzer" "rls")))
((cmake-mode cmake-ts-mode) . ("cmake-language-server"))
(vimrc-mode . ("vim-language-server" "--stdio"))
((python-mode python-ts-mode)

View file

@ -3451,14 +3451,13 @@ This function is intended for use in `after-change-functions'."
((parent-is "statement_block") parent-bol js-indent-level)
;; JSX
((parent-is "jsx_opening_element") parent js-indent-level)
((match "<" "jsx_fragment") parent 0)
((parent-is "jsx_fragment") parent js-indent-level)
((node-is "jsx_fragment") parent typescript-ts-mode-indent-offset)
((node-is "jsx_element") parent typescript-ts-mode-indent-offset)
((node-is "jsx_expression") parent typescript-ts-mode-indent-offset)
((node-is "jsx_self_closing_element") parent typescript-ts-mode-indent-offset)
((node-is "jsx_closing_element") parent 0)
((node-is "jsx_text") parent js-indent-level)
((parent-is "jsx_element") parent js-indent-level)
((node-is "/") parent 0)
((parent-is "jsx_self_closing_element") parent js-indent-level)))))
((node-is ">") parent 0)))))
(defvar js--treesit-keywords
'("as" "async" "await" "break" "case" "catch" "class" "const" "continue"

View file

@ -134,6 +134,13 @@ This should only be called after matching against `ruby-here-doc-beg-re'."
(defconst ruby-symbol-re (concat "[" ruby-symbol-chars "]")
"Regexp to match symbols.")
(defconst ruby-endless-method-head-re
(format " *\\(%s+\\.\\)?%s+[?!]? *\\(([^()]*)\\)? +="
ruby-symbol-re ruby-symbol-re)
"Regexp to match the beginning of an endless method definition.
It should match the part after \"def\" and until \"=\".")
(defvar ruby-use-smie t)
(make-obsolete-variable 'ruby-use-smie nil "28.1")
@ -351,7 +358,8 @@ This only affects the output of the command `ruby-toggle-block'."
(exp (exp1) (exp "," exp) (exp "=" exp)
(id " @ " exp))
(exp1 (exp2) (exp2 "?" exp1 ":" exp1))
(exp2 (exp3) (exp3 "." exp3))
(exp2 (exp3) (exp3 "." exp3)
(exp3 "def=" exp3))
(exp3 ("def" insts "end")
("begin" insts-rescue-insts "end")
("do" insts "end")
@ -468,7 +476,7 @@ This only affects the output of the command `ruby-toggle-block'."
"else" "elsif" "do" "end" "and")
'symbols))))
(memq (car (syntax-after pos)) '(7 15))
(looking-at "[([]\\|[-+!~:]\\(?:\\sw\\|\\s_\\)")))))
(looking-at "[([]\\|[-+!~:@$]\\(?:\\sw\\|\\s_\\)")))))
(defun ruby-smie--before-method-name ()
;; Only need to be accurate when method has keyword name.
@ -528,6 +536,9 @@ This only affects the output of the command `ruby-toggle-block'."
(ruby-smie--forward-token)) ;Fully redundant.
(t ";")))
((equal tok "&.") ".")
((and (equal tok "def")
(looking-at ruby-endless-method-head-re))
"def=")
(t tok)))))))))
(defun ruby-smie--backward-token ()
@ -575,6 +586,9 @@ This only affects the output of the command `ruby-toggle-block'."
(ruby-smie--backward-token)) ;Fully redundant.
(t ";")))
((equal tok "&.") ".")
((and (equal tok "def")
(looking-at (concat "def" ruby-endless-method-head-re)))
"def=")
(t tok)))))))
(defun ruby-smie--indent-to-stmt ()
@ -629,6 +643,11 @@ This only affects the output of the command `ruby-toggle-block'."
(not (ruby-smie--bosp)))
(forward-char -1))
(smie-indent-virtual))
((save-excursion
(and (smie-rule-parent-p " @ ")
(goto-char (nth 1 (smie-indent--parent)))
(smie-rule-prev-p "def=")
(cons 'column (- (current-column) 3)))))
(t (smie-rule-parent))))))
(`(:after . ,(or "(" "[" "{"))
;; FIXME: Shouldn't this be the default behavior of
@ -672,6 +691,12 @@ This only affects the output of the command `ruby-toggle-block'."
(and (smie-rule-parent-p ";" nil)
(smie-indent--hanging-p)
ruby-indent-level))
(`(:before . "=")
(save-excursion
(and (smie-rule-parent-p " @ ")
(goto-char (nth 1 (smie-indent--parent)))
(smie-rule-prev-p "def=")
(cons 'column (+ (current-column) ruby-indent-level -3)))))
(`(:after . ,(or "?" ":")) ruby-indent-level)
(`(:before . ,(guard (memq (intern-soft token) ruby-alignable-keywords)))
(when (not (ruby--at-indentation-p))
@ -1375,9 +1400,10 @@ With ARG, move backward multiple defuns. Negative ARG means
move forward."
(interactive "p")
(let (case-fold-search)
(and (re-search-backward (concat "^\\s *" ruby-defun-beg-re "\\_>")
nil t (or arg 1))
(beginning-of-line))))
(when (re-search-backward (concat "^\\s *" ruby-defun-beg-re "\\_>")
nil t (or arg 1))
(beginning-of-line)
t)))
(defun ruby-end-of-defun ()
"Move point to the end of the current defun.
@ -1631,7 +1657,7 @@ See `add-log-current-defun-function'."
(while (and (re-search-backward definition-re nil t)
(if (if (string-equal "def" (match-string 1))
;; We're inside a method.
(if (ruby-block-contains-point start)
(if (ruby-block-contains-point (1- start))
t
;; Try to match a method only once.
(setq definition-re module-re)

View file

@ -0,0 +1,371 @@
;;; rust-ts-mode.el --- tree-sitter support for Rust -*- lexical-binding: t; -*-
;; Copyright (C) 2022 Free Software Foundation, Inc.
;; Author : Randy Taylor <dev@rjt.dev>
;; Maintainer : Randy Taylor <dev@rjt.dev>
;; Created : December 2022
;; Keywords : rust languages tree-sitter
;; 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 <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;;; Code:
(require 'treesit)
(eval-when-compile (require 'rx))
(declare-function treesit-parser-create "treesit.c")
(declare-function treesit-induce-sparse-tree "treesit.c")
(declare-function treesit-node-child "treesit.c")
(declare-function treesit-node-child-by-field-name "treesit.c")
(declare-function treesit-node-start "treesit.c")
(declare-function treesit-node-type "treesit.c")
(defcustom rust-ts-mode-indent-offset 4
"Number of spaces for each indentation step in `rust-ts-mode'."
:version "29.1"
:type 'integer
:safe 'integerp
:group 'rust)
(defvar rust-ts-mode--syntax-table
(let ((table (make-syntax-table)))
(modify-syntax-entry ?+ "." table)
(modify-syntax-entry ?- "." table)
(modify-syntax-entry ?= "." table)
(modify-syntax-entry ?% "." table)
(modify-syntax-entry ?& "." table)
(modify-syntax-entry ?| "." table)
(modify-syntax-entry ?^ "." table)
(modify-syntax-entry ?! "." table)
(modify-syntax-entry ?@ "." table)
(modify-syntax-entry ?~ "." table)
(modify-syntax-entry ?< "." table)
(modify-syntax-entry ?> "." table)
(modify-syntax-entry ?/ ". 124b" table)
(modify-syntax-entry ?* ". 23" table)
(modify-syntax-entry ?\n "> b" table)
(modify-syntax-entry ?\^m "> b" table)
table)
"Syntax table for `rust-ts-mode'.")
(defvar rust-ts-mode--indent-rules
`((rust
((node-is ")") parent-bol 0)
((node-is "]") parent-bol 0)
((node-is "}") (and parent parent-bol) 0)
((parent-is "arguments") parent-bol rust-ts-mode-indent-offset)
((parent-is "await_expression") parent-bol rust-ts-mode-indent-offset)
((parent-is "array_expression") parent-bol rust-ts-mode-indent-offset)
((parent-is "binary_expression") parent-bol rust-ts-mode-indent-offset)
((parent-is "block") parent-bol rust-ts-mode-indent-offset)
((parent-is "declaration_list") parent-bol rust-ts-mode-indent-offset)
((parent-is "enum_variant_list") parent-bol rust-ts-mode-indent-offset)
((parent-is "field_declaration_list") parent-bol rust-ts-mode-indent-offset)
((parent-is "field_expression") parent-bol rust-ts-mode-indent-offset)
((parent-is "field_initializer_list") parent-bol rust-ts-mode-indent-offset)
((parent-is "let_declaration") parent-bol rust-ts-mode-indent-offset)
((parent-is "macro_definition") parent-bol rust-ts-mode-indent-offset)
((parent-is "parameters") parent-bol rust-ts-mode-indent-offset)
((parent-is "token_tree") parent-bol rust-ts-mode-indent-offset)
((parent-is "use_list") parent-bol rust-ts-mode-indent-offset)))
"Tree-sitter indent rules for `rust-ts-mode'.")
(defvar rust-ts-mode--builtin-macros
'("concat_bytes" "concat_idents" "const_format_args"
"format_args_nl" "log_syntax" "trace_macros" "assert" "assert_eq"
"assert_ne" "cfg" "column" "compile_error" "concat" "dbg"
"debug_assert" "debug_assert_eq" "debug_assert_ne" "env" "eprint"
"eprintln" "file" "format" "format_args" "include" "include_bytes"
"include_str" "is_x86_feature_detected" "line" "matches"
"module_path" "option_env" "panic" "print" "println" "stringify"
"thread_local" "todo" "try" "unimplemented" "unreachable" "vec"
"write" "writeln")
"Rust built-in macros for tree-sitter font-locking.")
(defvar rust-ts-mode--keywords
'("as" "async" "await" "break" "const" "continue" "dyn" "else"
"enum" "extern" "fn" "for" "if" "impl" "in" "let" "loop" "match"
"mod" "move" "pub" "ref" "return" "static" "struct" "trait" "type"
"union" "unsafe" "use" "where" "while" (crate) (self) (super)
(mutable_specifier))
"Rust keywords for tree-sitter font-locking.")
(defvar rust-ts-mode--operators
'("!" "!=" "%" "%=" "&" "&=" "&&" "*" "*=" "+" "+=" "," "-" "-="
"->" "." ".." "..=" "..." "/" "/=" ":" ";" "<<" "<<=" "<" "<="
"=" "==" "=>" ">" ">=" ">>" ">>=" "@" "^" "^=" "|" "|=" "||" "?")
"Rust operators for tree-sitter font-locking.")
(defvar rust-ts-mode--font-lock-settings
(treesit-font-lock-rules
:language 'rust
:feature 'attribute
'((attribute_item) @font-lock-constant-face
(inner_attribute_item) @font-lock-constant-face)
:language 'rust
:feature 'bracket
'((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face)
:language 'rust
:feature 'builtin
`((macro_invocation
macro: ((identifier) @font-lock-builtin-face
(:match ,(rx-to-string
`(seq bol
(or ,@rust-ts-mode--builtin-macros)
eol))
@font-lock-builtin-face)))
((identifier) @font-lock-type-face
(:match "^\\(:?Err\\|Ok\\|None\\|Some\\)$" @font-lock-type-face)))
:language 'rust
:feature 'comment
'(([(block_comment) (line_comment)]) @font-lock-comment-face)
:language 'rust
:feature 'constant
`((boolean_literal) @font-lock-constant-face
((identifier) @font-lock-constant-face
(:match "^[A-Z][A-Z\\d_]*$" @font-lock-constant-face)))
:language 'rust
:feature 'delimiter
'((["," "." ";" ":" "::"]) @font-lock-delimiter-face)
:language 'rust
:feature 'function
'((call_expression
function:
[(identifier) @font-lock-function-name-face
(field_expression
field: (field_identifier) @font-lock-function-name-face)
(scoped_identifier
name: (identifier) @font-lock-function-name-face)])
(function_item (identifier) @font-lock-function-name-face)
(generic_function
function: [(identifier) @font-lock-function-name-face
(field_expression
field: (field_identifier) @font-lock-function-name-face)
(scoped_identifier
name: (identifier) @font-lock-function-name-face)])
(macro_definition "macro_rules!" @font-lock-constant-face)
(macro_definition (identifier) @font-lock-preprocessor-face)
(macro_invocation macro: (identifier) @font-lock-preprocessor-face))
:language 'rust
:feature 'keyword
`([,@rust-ts-mode--keywords] @font-lock-keyword-face)
:language 'rust
:feature 'number
'([(float_literal) (integer_literal)] @font-lock-number-face)
:language 'rust
:feature 'operator
`([,@rust-ts-mode--operators] @font-lock-operator-face)
:language 'rust
:feature 'string
'([(char_literal)
(raw_string_literal)
(string_literal)] @font-lock-string-face)
:language 'rust
:feature 'type
`((call_expression
function: (scoped_identifier
path: (identifier) @font-lock-type-face))
(enum_variant name: (identifier) @font-lock-type-face)
(match_arm
pattern: (match_pattern (_ type: (identifier) @font-lock-type-face)))
(match_arm
pattern: (match_pattern
(_ type: (scoped_identifier
path: (identifier) @font-lock-type-face))))
(mod_item name: (identifier) @font-lock-constant-face)
(primitive_type) @font-lock-type-face
(type_identifier) @font-lock-type-face
(scoped_identifier name: (identifier) @font-lock-type-face)
(scoped_identifier path: (identifier) @font-lock-constant-face)
(scoped_identifier
(scoped_identifier
path: (identifier) @font-lock-constant-face))
((scoped_identifier
path: [(identifier) @font-lock-type-face
(scoped_identifier
name: (identifier) @font-lock-type-face)])
(:match "^[A-Z]" @font-lock-type-face))
(scoped_type_identifier path: (identifier) @font-lock-constant-face)
(scoped_use_list
path: [(identifier) @font-lock-constant-face
(scoped_identifier (identifier) @font-lock-constant-face)])
(type_identifier) @font-lock-type-face
(use_as_clause alias: (identifier) @font-lock-type-face)
(use_list (identifier) @font-lock-type-face))
:language 'rust
:feature 'variable
'((identifier) @font-lock-variable-name-face
;; Everything in a token_tree is an identifier.
(token_tree (identifier) @default))
:language 'rust
:feature 'escape-sequence
:override t
'((escape_sequence) @font-lock-escape-face)
:language 'rust
:feature 'property
:override t
'((field_identifier) @font-lock-property-face
(shorthand_field_initializer (identifier) @font-lock-property-face))
:language 'rust
:feature 'error
:override t
'((ERROR) @font-lock-warning-face))
"Tree-sitter font-lock settings for `rust-ts-mode'.")
(defun rust-ts-mode--imenu ()
"Return Imenu alist for the current buffer."
(let* ((node (treesit-buffer-root-node))
(enum-tree (treesit-induce-sparse-tree
node "enum_item" nil))
(enum-index (rust-ts-mode--imenu-1 enum-tree))
(func-tree (treesit-induce-sparse-tree
node "function_item" nil))
(func-index (rust-ts-mode--imenu-1 func-tree))
(impl-tree (treesit-induce-sparse-tree
node "impl_item" nil))
(impl-index (rust-ts-mode--imenu-1 impl-tree))
(mod-tree (treesit-induce-sparse-tree
node "mod_item" nil))
(mod-index (rust-ts-mode--imenu-1 mod-tree))
(struct-tree (treesit-induce-sparse-tree
node "struct_item" nil))
(struct-index (rust-ts-mode--imenu-1 struct-tree))
(type-tree (treesit-induce-sparse-tree
node "type_item" nil))
(type-index (rust-ts-mode--imenu-1 type-tree)))
(append
(when mod-index `(("Module" . ,mod-index)))
(when enum-index `(("Enum" . ,enum-index)))
(when impl-index `(("Impl" . ,impl-index)))
(when type-index `(("Type" . ,type-index)))
(when struct-index `(("Struct" . ,struct-index)))
(when func-index `(("Fn" . ,func-index))))))
(defun rust-ts-mode--imenu-1 (node)
"Helper for `rust-ts-mode--imenu'.
Find string representation for NODE and set marker, then recurse
the subtrees."
(let* ((ts-node (car node))
(children (cdr node))
(subtrees (mapcan #'rust-ts-mode--imenu-1
children))
(name (when ts-node
(pcase (treesit-node-type ts-node)
("enum_item"
(treesit-node-text
(treesit-node-child-by-field-name ts-node "name") t))
("function_item"
(treesit-node-text
(treesit-node-child-by-field-name ts-node "name") t))
("impl_item"
(let ((trait-node (treesit-node-child-by-field-name ts-node "trait")))
(concat
(treesit-node-text
trait-node t)
(when trait-node
" for ")
(treesit-node-text
(treesit-node-child-by-field-name ts-node "type") t))))
("mod_item"
(treesit-node-text
(treesit-node-child-by-field-name ts-node "name") t))
("struct_item"
(treesit-node-text
(treesit-node-child-by-field-name ts-node "name") t))
("type_item"
(treesit-node-text
(treesit-node-child-by-field-name ts-node "name") t)))))
(marker (when ts-node
(set-marker (make-marker)
(treesit-node-start ts-node)))))
(cond
((or (null ts-node) (null name)) subtrees)
(subtrees
`((,name ,(cons name marker) ,@subtrees)))
(t
`((,name . ,marker))))))
;;;###autoload
(add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-ts-mode))
;;;###autoload
(define-derived-mode rust-ts-mode prog-mode "Rust"
"Major mode for editing Rust, powered by tree-sitter."
:group 'rust
:syntax-table rust-ts-mode--syntax-table
(when (treesit-ready-p 'rust)
(treesit-parser-create 'rust)
;; Comments.
(setq-local comment-start "// ")
(setq-local comment-end "")
(setq-local comment-start-skip (rx (or (seq "/" (+ "/"))
(seq "/" (+ "*")))
(* (syntax whitespace))))
(setq-local comment-end-skip
(rx (* (syntax whitespace))
(group (or (syntax comment-end)
(seq (+ "*") "/")))))
;; Font-lock.
(setq-local treesit-font-lock-settings rust-ts-mode--font-lock-settings)
(setq-local treesit-font-lock-feature-list
'(( comment)
( keyword string)
( attribute builtin constant escape-sequence
function number property type variable)
( bracket delimiter error operator)))
;; Imenu.
(setq-local imenu-create-index-function #'rust-ts-mode--imenu)
(setq-local which-func-functions nil)
;; Indent.
(setq-local indent-tabs-mode nil
treesit-simple-indent-rules rust-ts-mode--indent-rules)
;; Navigation.
(setq-local treesit-defun-type-regexp
(regexp-opt '("enum_item"
"function_item"
"impl_item"
"struct_item")))
(treesit-major-mode-setup)))
(provide 'rust-ts-mode)
;;; rust-ts-mode.el ends here

View file

@ -4526,7 +4526,8 @@ optionally is saved to the user's init file."
"Run PRODUCT interpreter as an inferior process.
If buffer `*SQL*' exists but no process is running, make a new process.
If buffer exists and a process is running, just switch to buffer `*SQL*'.
If buffer exists and a process is running, just make sure buffer `*SQL*'
is displayed.
To specify the SQL product, prefix the call with
\\[universal-argument]. To set the buffer name as well, prefix

View file

@ -95,13 +95,13 @@ Argument LANGUAGE is either `typescript' or `tsx'."
((parent-is "binary_expression") parent-bol typescript-ts-mode-indent-offset)
,@(when (eq language 'tsx)
`(((parent-is "jsx_opening_element") parent typescript-ts-mode-indent-offset)
((match "<" "jsx_fragment") parent 0)
((parent-is "jsx_fragment") parent typescript-ts-mode-indent-offset)
`(((node-is "jsx_fragment") parent typescript-ts-mode-indent-offset)
((node-is "jsx_element") parent typescript-ts-mode-indent-offset)
((node-is "jsx_expression") parent typescript-ts-mode-indent-offset)
((node-is "jsx_self_closing_element") parent typescript-ts-mode-indent-offset)
((node-is "jsx_closing_element") parent 0)
((parent-is "jsx_element") parent typescript-ts-mode-indent-offset)
((node-is "/") parent 0)
((parent-is "jsx_self_closing_element") parent typescript-ts-mode-indent-offset)))
((node-is ">") parent 0)))
(no-node parent-bol 0))))
(defvar typescript-ts-mode--keywords
@ -231,7 +231,7 @@ Argument LANGUAGE is either `typescript' or `tsx'."
(arguments (identifier) @font-lock-variable-name-face)
(parenthesized_expression (identifier) @font-lock-variable-name-face)
(parenthesized_expression (_ (identifier)) @font-lock-variable-name-face))
(parenthesized_expression (_ (identifier) @font-lock-variable-name-face)))
:language language
:override t
@ -316,10 +316,7 @@ Argument LANGUAGE is either `typescript' or `tsx'."
:language language
:feature 'escape-sequence
:override t
'((escape_sequence) @font-lock-escape-face)
))
'((escape_sequence) @font-lock-escape-face)))
;;;###autoload
(add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-ts-mode))

View file

@ -3956,9 +3956,10 @@ detailed description.
(defun internal--with-narrowing (start end body &optional tag)
"Helper function for `with-narrowing', which see."
(save-restriction
(narrow-to-region start end)
(when tag (narrowing-lock tag))
(funcall body)))
(progn
(narrow-to-region start end)
(if tag (narrowing-lock tag))
(funcall body))))
(defun find-tag-default-bounds ()
"Determine the boundaries of the default tag, based on text at point.

View file

@ -1047,7 +1047,6 @@ See `treesit-simple-indent-presets'.")
(or (null node-index-max)
(<= (treesit-node-index node)
node-index-max))))))
;; TODO: Document if genuinely useful.
(cons 'n-p-gp
(lambda (node-t parent-t grand-parent-t)
(lambda (node parent &rest _)
@ -1108,12 +1107,6 @@ See `treesit-simple-indent-presets'.")
(re-search-forward comment-start-skip)
(skip-syntax-backward "-")
(point))))
(cons 'comment-start-skip
(lambda (_n parent &rest _)
(save-excursion
(goto-char (treesit-node-start parent))
(re-search-forward comment-start-skip)
(point))))
;; TODO: Document.
(cons 'grand-parent
(lambda (_n parent &rest _)
@ -1188,6 +1181,10 @@ no-node
Checks that NODE's type matches regexp TYPE.
\(n-p-gp NODE-TYPE PARENT-TYPE GRANDPARENT-TYPE)
Checks that NODE, its parent, and its grandparent's type.
\(query QUERY)
Queries PARENT with QUERY, and checks if NODE is
@ -1230,14 +1227,9 @@ point-min
comment-start
Returns the position after a match for `treesit-comment-start'.
Assumes PARENT is a comment node.
comment-start-skip
Goes to the position that comment-start would return, skips
whitespace after that, and returns the resulting position.
Assumes PARENT is a comment node.")
Goes to the position that `comment-start-skip' would return,
skips whitespace backwards, and returns the resulting
position. Assumes PARENT is a comment node.")
(defun treesit--simple-indent-eval (exp)
"Evaluate EXP.
@ -2046,8 +2038,8 @@ to the offending pattern and highlight the pattern."
(goto-char (point-min))
(insert (format "%s: %d\n" message start))
(forward-char start)))
(pop-to-buffer buf)))))
(view-mode))
(pop-to-buffer buf)
(view-mode))))))
;;; Explorer

View file

@ -776,7 +776,8 @@ Default value of MODIFIERS is `shift-super'."
(defconst windmove--default-keybindings-type
`(choice (const :tag "Don't bind" nil)
(cons :tag "Bind using"
(key-sequence :tag "Prefix")
(choice (key-sequence :tag "Prefix")
(const :tag "No Prefix" nil))
(set :tag "Modifier"
:greedy t
;; See `(elisp) Keyboard Events'

View file

@ -2162,17 +2162,14 @@ the font."
(let* ((window-width (window-body-width window t))
(font-width (window-font-width window face))
(ncols (- (/ window-width font-width)
(ceiling (line-number-display-width 'columns)))))
(ceiling (line-number-display-width 'columns))))
(fringes (window-fringes window))
(lfringe (car fringes))
(rfringe (nth 1 fringes)))
(if (and (display-graphic-p)
overflow-newline-into-fringe
(not
(or (eq left-fringe-width 0)
(and (null left-fringe-width)
(= (frame-parameter nil 'left-fringe) 0))))
(not
(or (eq right-fringe-width 0)
(and (null right-fringe-width)
(= (frame-parameter nil 'right-fringe) 0)))))
(not (eq lfringe 0))
(not (eq rfringe 0)))
ncols
;; FIXME: This should remove 1 more column when there are no
;; fringes, lines are truncated, and the window is hscrolled,

View file

@ -2428,16 +2428,11 @@ DEFUN ("make-directory-internal", Fmake_directory_internal,
(Lisp_Object directory)
{
const char *dir;
Lisp_Object handler;
Lisp_Object encoded_dir;
CHECK_STRING (directory);
directory = Fexpand_file_name (directory, Qnil);
handler = Ffind_file_name_handler (directory, Qmake_directory_internal);
if (!NILP (handler))
return call2 (handler, Qmake_directory_internal, directory);
encoded_dir = ENCODE_FILE (directory);
dir = SSDATA (encoded_dir);

View file

@ -5479,11 +5479,16 @@ to the specified file name if a suffix is allowed or required. */);
DEFVAR_LISP ("dynamic-library-suffixes", Vdynamic_library_suffixes,
doc: /* A list of suffixes for loadable dynamic libraries. */);
Vdynamic_library_suffixes =
Fcons (build_pure_c_string (DYNAMIC_LIB_SECONDARY_SUFFIX), Qnil);
Vdynamic_library_suffixes =
Fcons (build_pure_c_string (DYNAMIC_LIB_SUFFIX),
Vdynamic_library_suffixes);
#ifndef MSDOS
Vdynamic_library_suffixes
= Fcons (build_pure_c_string (DYNAMIC_LIB_SECONDARY_SUFFIX), Qnil);
Vdynamic_library_suffixes
= Fcons (build_pure_c_string (DYNAMIC_LIB_SUFFIX),
Vdynamic_library_suffixes);
#else
Vdynamic_library_suffixes = Qnil;
#endif
DEFVAR_LISP ("load-file-rep-suffixes", Vload_file_rep_suffixes,
doc: /* List of suffixes that indicate representations of \

View file

@ -52,7 +52,6 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#undef ts_node_named_descendant_for_byte_range
#undef ts_node_next_named_sibling
#undef ts_node_next_sibling
#undef ts_node_parent
#undef ts_node_prev_named_sibling
#undef ts_node_prev_sibling
#undef ts_node_start_byte
@ -76,7 +75,9 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#undef ts_query_predicates_for_pattern
#undef ts_query_string_value_for_id
#undef ts_set_allocator
#undef ts_tree_cursor_copy
#undef ts_tree_cursor_current_node
#undef ts_tree_cursor_delete
#undef ts_tree_cursor_goto_first_child
#undef ts_tree_cursor_goto_next_sibling
#undef ts_tree_cursor_goto_parent
@ -109,7 +110,6 @@ DEF_DLL_FN (TSNode, ts_node_named_descendant_for_byte_range,
(TSNode, uint32_t, uint32_t));
DEF_DLL_FN (TSNode, ts_node_next_named_sibling, (TSNode));
DEF_DLL_FN (TSNode, ts_node_next_sibling, (TSNode));
DEF_DLL_FN (TSNode, ts_node_parent, (TSNode));
DEF_DLL_FN (TSNode, ts_node_prev_named_sibling, (TSNode));
DEF_DLL_FN (TSNode, ts_node_prev_sibling, (TSNode));
DEF_DLL_FN (uint32_t, ts_node_start_byte, (TSNode));
@ -143,7 +143,9 @@ DEF_DLL_FN (const char *, ts_query_string_value_for_id,
(const TSQuery *, uint32_t, uint32_t *));
DEF_DLL_FN (void, ts_set_allocator,
(void *(*)(size_t), void *(*)(size_t, size_t), void *(*)(void *, size_t), void (*)(void *)));
DEF_DLL_FN (TSTreeCursor, ts_tree_cursor_copy, (const TSTreeCursor *));
DEF_DLL_FN (TSNode, ts_tree_cursor_current_node, (const TSTreeCursor *));
DEF_DLL_FN (void, ts_tree_cursor_delete, (const TSTreeCursor *));
DEF_DLL_FN (bool, ts_tree_cursor_goto_first_child, (TSTreeCursor *));
DEF_DLL_FN (bool, ts_tree_cursor_goto_next_sibling, (TSTreeCursor *));
DEF_DLL_FN (bool, ts_tree_cursor_goto_parent, (TSTreeCursor *));
@ -182,7 +184,6 @@ init_treesit_functions (void)
LOAD_DLL_FN (library, ts_node_named_descendant_for_byte_range);
LOAD_DLL_FN (library, ts_node_next_named_sibling);
LOAD_DLL_FN (library, ts_node_next_sibling);
LOAD_DLL_FN (library, ts_node_parent);
LOAD_DLL_FN (library, ts_node_prev_named_sibling);
LOAD_DLL_FN (library, ts_node_prev_sibling);
LOAD_DLL_FN (library, ts_node_start_byte);
@ -206,7 +207,9 @@ init_treesit_functions (void)
LOAD_DLL_FN (library, ts_query_predicates_for_pattern);
LOAD_DLL_FN (library, ts_query_string_value_for_id);
LOAD_DLL_FN (library, ts_set_allocator);
LOAD_DLL_FN (library, ts_tree_cursor_copy);
LOAD_DLL_FN (library, ts_tree_cursor_current_node);
LOAD_DLL_FN (library, ts_tree_cursor_delete);
LOAD_DLL_FN (library, ts_tree_cursor_goto_first_child);
LOAD_DLL_FN (library, ts_tree_cursor_goto_next_sibling);
LOAD_DLL_FN (library, ts_tree_cursor_goto_parent);
@ -239,7 +242,6 @@ init_treesit_functions (void)
#define ts_node_named_descendant_for_byte_range fn_ts_node_named_descendant_for_byte_range
#define ts_node_next_named_sibling fn_ts_node_next_named_sibling
#define ts_node_next_sibling fn_ts_node_next_sibling
#define ts_node_parent fn_ts_node_parent
#define ts_node_prev_named_sibling fn_ts_node_prev_named_sibling
#define ts_node_prev_sibling fn_ts_node_prev_sibling
#define ts_node_start_byte fn_ts_node_start_byte
@ -263,7 +265,9 @@ init_treesit_functions (void)
#define ts_query_predicates_for_pattern fn_ts_query_predicates_for_pattern
#define ts_query_string_value_for_id fn_ts_query_string_value_for_id
#define ts_set_allocator fn_ts_set_allocator
#define ts_tree_cursor_copy fn_ts_tree_cursor_copy
#define ts_tree_cursor_current_node fn_ts_tree_cursor_current_node
#define ts_tree_cursor_delete fn_ts_tree_cursor_delete
#define ts_tree_cursor_goto_first_child fn_ts_tree_cursor_goto_first_child
#define ts_tree_cursor_goto_next_sibling fn_ts_tree_cursor_goto_next_sibling
#define ts_tree_cursor_goto_parent fn_ts_tree_cursor_goto_parent
@ -288,7 +292,7 @@ init_treesit_functions (void)
slow enough to make insignificant any performance advantages from
using the cursor. Not exposing the cursor also minimizes the
number of new types this adds to Emacs Lisp; currently, this adds
only the parser and node types.
only the parser, node, and compiled query types.
- Because updating the change is handled on the C level as each
change is made in the buffer, there is no way for Lisp to update
@ -1758,6 +1762,8 @@ If NODE is nil, return nil. */)
return build_string (string);
}
static TSTreeCursor treesit_cursor_helper (TSNode, Lisp_Object);
DEFUN ("treesit-node-parent",
Ftreesit_node_parent, Streesit_node_parent, 1, 1, 0,
doc: /* Return the immediate parent of NODE.
@ -1768,13 +1774,18 @@ Return nil if NODE has no parent. If NODE is nil, return nil. */)
treesit_check_node (node);
treesit_initialize ();
Lisp_Object return_value = Qnil;
TSNode treesit_node = XTS_NODE (node)->node;
TSNode parent = ts_node_parent (treesit_node);
if (ts_node_is_null (parent))
return Qnil;
return make_treesit_node (XTS_NODE (node)->parser, parent);
Lisp_Object parser = XTS_NODE (node)->parser;
TSTreeCursor cursor = treesit_cursor_helper (treesit_node, parser);
if (ts_tree_cursor_goto_parent (&cursor))
{
TSNode parent = ts_tree_cursor_current_node (&cursor);
return_value = make_treesit_node (parser, parent);
}
ts_tree_cursor_delete (&cursor);
return return_value;
}
DEFUN ("treesit-node-child",
@ -2620,63 +2631,210 @@ the query. */)
/*** Navigation */
/* Return the next/previous named/unnamed sibling of NODE. FORWARD
controls the direction and NAMED controls the nameness. */
static TSNode
treesit_traverse_sibling_helper (TSNode node, bool forward, bool named)
static inline void
treesit_assume_true (bool val)
{
if (forward)
{
if (named)
return ts_node_next_named_sibling (node);
else
return ts_node_next_sibling (node);
}
else
{
if (named)
return ts_node_prev_named_sibling (node);
else
return ts_node_prev_sibling (node);
}
eassert (val == true);
}
/* Return the first/last named/unnamed child of NODE. FORWARD controls
the direction and NAMED controls the nameness. */
static TSNode
treesit_traverse_child_helper (TSNode node, bool forward, bool named)
/* Create a TSTreeCursor pointing at NODE. PARSER is the lisp parser
that produced NODE.
The reason we need this instead of simply using ts_tree_cursor_new
is that we have to create the cursor on the root node and traverse
down to NODE, in order to record the correct stack of parent nodes.
Otherwise going to sibling or parent of NODE wouldn't work.
(Wow perfect filling.) */
static TSTreeCursor
treesit_cursor_helper (TSNode node, Lisp_Object parser)
{
if (forward)
uint32_t end_pos = ts_node_end_byte (node);
TSNode root = ts_tree_root_node (XTS_PARSER (parser)->tree);
TSTreeCursor cursor = ts_tree_cursor_new (root);
TSNode cursor_node = ts_tree_cursor_current_node (&cursor);
/* This is like treesit-node-at. We go down from the root node,
either to first child or next sibling, repeatedly, and finally
arrive at NODE. */
while (!ts_node_eq (node, cursor_node))
{
if (named)
return ts_node_named_child (node, 0);
else
return ts_node_child (node, 0);
}
else
{
if (named)
treesit_assume_true (ts_tree_cursor_goto_first_child (&cursor));
cursor_node = ts_tree_cursor_current_node (&cursor);
/* ts_tree_cursor_goto_first_child_for_byte is not reliable, so
we just go through each sibling. */
while (ts_node_is_missing (cursor_node)
|| ts_node_end_byte (cursor_node) < end_pos)
{
uint32_t count = ts_node_named_child_count (node);
uint32_t idx = count == 0 ? 0 : count - 1;
return ts_node_named_child (node, idx);
/* A "missing" node has zero width, so it's possible that
its end = NODE.end but it's not NODE, so we skip them.
But we need to make sure this missing node is not the
node we are looking for before skipping it. */
if (ts_node_is_missing (cursor_node)
&& ts_node_eq (node, cursor_node))
return cursor;
treesit_assume_true (ts_tree_cursor_goto_next_sibling (&cursor));
cursor_node = ts_tree_cursor_current_node (&cursor);
}
else
/* Right now CURSOR.end >= NODE.end. But what if CURSOR.end =
NODE.end, and there are missing nodes after CURSOR, and the
missing node after CURSOR is the NODE we are looking for??
Well, create a probe and look ahead. (This is tested by
treesit-cursor-helper-with-missing-node.) */
TSTreeCursor probe = ts_tree_cursor_copy (&cursor);
TSNode probe_node;
while (ts_tree_cursor_goto_next_sibling (&probe))
{
uint32_t count = ts_node_child_count (node);
uint32_t idx = count == 0 ? 0 : count - 1;
return ts_node_child (node, idx);
probe_node = ts_tree_cursor_current_node (&probe);
if (!ts_node_is_missing (probe_node))
break;
if (ts_node_eq (probe_node, node))
{
ts_tree_cursor_delete (&cursor);
return probe;
}
}
ts_tree_cursor_delete (&probe);
}
return cursor;
}
/* Return true if NODE matches PRED. PRED can be a string or a
function. This function assumes PRED is either a string or a
function. */
/* Move CURSOR to the next/previous sibling. FORWARD controls the
direction. NAMED controls the namedness. If there is a valid
sibling, move CURSOR to it and return true, otherwise return false.
When false is returned, CURSOR points to a sibling node of the node
we started at, but exactly which is undefined. */
static bool
treesit_traverse_match_predicate (TSNode node, Lisp_Object pred,
Lisp_Object parser)
treesit_traverse_sibling_helper (TSTreeCursor *cursor,
bool forward, bool named)
{
if (forward)
{
if (!named)
return ts_tree_cursor_goto_next_sibling (cursor);
/* Else named... */
while (ts_tree_cursor_goto_next_sibling (cursor))
{
if (ts_node_is_named (ts_tree_cursor_current_node (cursor)))
return true;
}
return false;
}
else /* Backward. */
{
/* Go to first child and go through each sibling, until we find
the one just before the starting node. */
TSNode start = ts_tree_cursor_current_node (cursor);
if (!ts_tree_cursor_goto_parent (cursor))
return false;
treesit_assume_true (ts_tree_cursor_goto_first_child (cursor));
/* Now CURSOR is at the first child. If we started at the first
child, then there is no further siblings. */
TSNode first_child = ts_tree_cursor_current_node (cursor);
if (ts_node_eq (first_child, start))
return false;
/* PROBE is always DELTA siblings ahead of CURSOR. */
TSTreeCursor probe = ts_tree_cursor_copy (cursor);
/* This is position of PROBE minus position of CURSOR. */
ptrdiff_t delta = 0;
TSNode probe_node;
TSNode cursor_node;
while (ts_tree_cursor_goto_next_sibling (&probe))
{
/* Move PROBE forward, if it equals to the starting node,
CURSOR points to the node we want (prev valid sibling of
the starting node). */
delta++;
probe_node = ts_tree_cursor_current_node (&probe);
/* PROBE matched, depending on NAMED, return true/false. */
if (ts_node_eq (probe_node, start))
{
ts_tree_cursor_delete (&probe);
cursor_node = ts_tree_cursor_current_node (cursor);
ts_tree_cursor_delete (&probe);
return (!named || (named && ts_node_is_named (cursor_node)));
}
/* PROBE didn't match, move CURSOR forward to PROBE's
position, but if we are looking for named nodes, only
move CURSOR to PROBE if PROBE is at a named node. */
if (!named || (named && ts_node_is_named (probe_node)))
for (; delta > 0; delta--)
treesit_assume_true (ts_tree_cursor_goto_next_sibling (cursor));
}
ts_tree_cursor_delete (&probe);
return false;
}
}
/* Move CURSOR to the first/last child. FORWARD controls the
direction. NAMED controls the namedness. If there is a valid
child, move CURSOR to it and return true, otherwise don't move
CURSOR and return false. */
static bool
treesit_traverse_child_helper (TSTreeCursor *cursor,
bool forward, bool named)
{
if (forward)
{
if (!named)
return ts_tree_cursor_goto_first_child (cursor);
else
{
if (!ts_tree_cursor_goto_first_child (cursor))
return false;
/* After this point, if you return false, make sure to go
back to parent. */
TSNode first_child = ts_tree_cursor_current_node (cursor);
if (ts_node_is_named (first_child))
return true;
if (treesit_traverse_sibling_helper (cursor, true, true))
return true;
else
{
treesit_assume_true (ts_tree_cursor_goto_parent (cursor));
return false;
}
}
}
else /* Backward. */
{
if (!ts_tree_cursor_goto_first_child (cursor))
return false;
/* After this point, if you return false, make sure to go
back to parent. */
/* First go to the last child. */
while (ts_tree_cursor_goto_next_sibling (cursor));
if (!named)
return true;
/* Else named... */
if (treesit_traverse_sibling_helper(cursor, false, true))
return true;
else
{
treesit_assume_true (ts_tree_cursor_goto_parent (cursor));
return false;
}
}
}
/* Return true if the node at CURSOR matches PRED. PRED can be a
string or a function. This function assumes PRED is either a
string or a function. If NAMED is true, also check that the node
is named. */
static bool
treesit_traverse_match_predicate (TSTreeCursor *cursor, Lisp_Object pred,
Lisp_Object parser, bool named)
{
TSNode node = ts_tree_cursor_current_node (cursor);
if (named && !ts_node_is_named (node))
return false;
if (STRINGP (pred))
{
const char *type = ts_node_type (node);
@ -2687,73 +2845,60 @@ treesit_traverse_match_predicate (TSNode node, Lisp_Object pred,
Lisp_Object lisp_node = make_treesit_node (parser, node);
return !NILP (CALLN (Ffuncall, pred, lisp_node));
}
}
/* Traverse the parse tree starting from ROOT (but ROOT is not
matches against PRED). PRED can be a function (takes a node and
returns nil/non-nil),or a string (treated as regexp matching the
node's type, ignores case, must be all single byte characters). If
the node satisfies PRED , terminate, set ROOT to that node, and
return true. If no node satisfies PRED, return FALSE. PARSER is
the parser of ROOT.
/* Traverse the parse tree starting from CURSOR. PRED can be a
function (takes a node and returns nil/non-nil), or a string
(treated as regexp matching the node's type, must be all single
byte characters). If the node satisfies PRED, leave CURSOR on that
node and return true. If no node satisfies PRED, move CURSOR back
to starting position and return false.
LIMIT is the number of levels we descend in the tree. FORWARD
controls the direction in which we traverse the tree, true means
forward, false backward. If NAMED is true, only traverse named
nodes, if false, all nodes. If SKIP_ROOT is true, don't match
ROOT. */
forward, false backward. If SKIP_ROOT is true, don't match ROOT.
*/
static bool
treesit_search_dfs (TSNode *root, Lisp_Object pred, Lisp_Object parser,
bool named, bool forward, ptrdiff_t limit,
treesit_search_dfs (TSTreeCursor *cursor,
Lisp_Object pred, Lisp_Object parser,
bool forward, bool named, ptrdiff_t limit,
bool skip_root)
{
/* TSTreeCursor doesn't allow us to move backward, so we can't use
it. */
TSNode node = *root;
if (!skip_root
&& treesit_traverse_match_predicate (cursor, pred, parser, named))
return true;
if (!skip_root && treesit_traverse_match_predicate (node, pred, parser))
{
*root = node;
return true;
}
if (limit <= 0)
if (limit == 0)
return false;
else
{
int count = (named
? ts_node_named_child_count (node)
: ts_node_child_count (node));
for (int offset = 0; offset < count; offset++)
{
uint32_t idx = forward ? offset : count - offset - 1;
TSNode child = (named
? ts_node_named_child (node, idx)
: ts_node_child (node, idx));
if (!ts_node_is_null (child)
&& treesit_search_dfs (&child, pred, parser, named,
forward, limit - 1, false))
{
*root = child;
return true;
}
}
return false;
if (!treesit_traverse_child_helper (cursor, forward, named))
return false;
/* After this point, if you return false, make sure to go back to
parent. */
do /* Iterate through each child. */
{
if (treesit_search_dfs (cursor, pred, parser, forward,
named, limit - 1, false))
return true;
}
while (treesit_traverse_sibling_helper (cursor, forward, false));
/* No match in any child's subtree, go back to starting node. */
treesit_assume_true (ts_tree_cursor_goto_parent (cursor));
return false;
}
/* Go through the whole tree linearly, leaf-first, starting from
START. PRED, PARSER, NAMED, FORWARD are the same as in
ts_search_subtre. If UP_ONLY is true, never go to children, only
sibling and parents. */
ts_search_subtree. If a match is found, leave CURSOR at that node,
and return true, if no match is found, return false, and CURSOR's
position is undefined. */
static bool
treesit_search_forward (TSNode *start, Lisp_Object pred, Lisp_Object parser,
bool named, bool forward)
treesit_search_forward (TSTreeCursor *cursor,
Lisp_Object pred, Lisp_Object parser,
bool forward, bool named)
{
TSNode node = *start;
/* We don't search for subtree and always search from the leaf
nodes. This way repeated call of this function traverses each
node in the tree once and only once:
@ -2763,39 +2908,26 @@ treesit_search_forward (TSNode *start, Lisp_Object pred, Lisp_Object parser,
bool initial = true;
while (true)
{
if (!initial /* We don't match START. */
&& treesit_traverse_match_predicate (node, pred, parser))
{
*start = node;
return true;
}
if (!initial /* We don't match the starting node. */
&& treesit_traverse_match_predicate (cursor, pred, parser, named))
return true;
initial = false;
TSNode next = treesit_traverse_sibling_helper (node, forward, named);
while (ts_node_is_null (next))
/* Try going to the next sibling, if there is no next sibling,
go to parent and try again. */
while (!treesit_traverse_sibling_helper (cursor, forward, named))
{
/* There is no next sibling, go to parent. */
node = ts_node_parent (node);
if (ts_node_is_null (node))
if (!ts_tree_cursor_goto_parent (cursor))
return false;
if (treesit_traverse_match_predicate (node, pred, parser))
{
*start = node;
if (treesit_traverse_match_predicate (cursor, pred, parser, named))
return true;
}
next = treesit_traverse_sibling_helper (node, forward, named);
}
/* We are at the next sibling, deep dive into the first leaf
node. */
TSNode next_next = treesit_traverse_child_helper (next, forward, named);
while (!ts_node_is_null (next_next))
{
next = next_next;
next_next = treesit_traverse_child_helper (next, forward, named);
}
/* At this point NEXT is a leaf node. */
node = next;
while (treesit_traverse_child_helper (cursor, forward, false));
/* At this point CURSOR is at a leaf node. */
}
}
@ -2823,7 +2955,7 @@ Return the first matched node, or nil if none matches. */)
CHECK_SYMBOL (all);
CHECK_SYMBOL (backward);
/* We use a default limit to 1000. See bug#59426 for the
/* We use a default limit of 1000. See bug#59426 for the
discussion. */
ptrdiff_t the_limit = 1000;
if (!NILP (limit))
@ -2834,13 +2966,17 @@ Return the first matched node, or nil if none matches. */)
treesit_initialize ();
TSNode treesit_node = XTS_NODE (node)->node;
Lisp_Object parser = XTS_NODE (node)->parser;
if (treesit_search_dfs (&treesit_node, predicate, parser, NILP (all),
NILP (backward), the_limit, false))
return make_treesit_node (parser, treesit_node);
else
return Qnil;
Lisp_Object return_value = Qnil;
TSTreeCursor cursor = treesit_cursor_helper (XTS_NODE (node)->node, parser);
if (treesit_search_dfs (&cursor, predicate, parser, NILP (backward),
NILP (all), the_limit, false))
{
TSNode node = ts_tree_cursor_current_node (&cursor);
return_value = make_treesit_node (parser, node);
}
ts_tree_cursor_delete (&cursor);
return return_value;
}
DEFUN ("treesit-search-forward",
@ -2884,13 +3020,17 @@ always traverse leaf nodes first, then upwards. */)
treesit_initialize ();
TSNode treesit_start = XTS_NODE (start)->node;
Lisp_Object parser = XTS_NODE (start)->parser;
if (treesit_search_forward (&treesit_start, predicate, parser, NILP (all),
NILP (backward)))
return make_treesit_node (parser, treesit_start);
else
return Qnil;
Lisp_Object return_value = Qnil;
TSTreeCursor cursor = treesit_cursor_helper (XTS_NODE (start)->node, parser);
if (treesit_search_forward (&cursor, predicate, parser,
NILP (backward), NILP (all)))
{
TSNode node = ts_tree_cursor_current_node (&cursor);
return_value = make_treesit_node (parser, node);
}
ts_tree_cursor_delete (&cursor);
return return_value;
}
/* Recursively traverse the tree under CURSOR, and append the result
@ -2902,13 +3042,12 @@ treesit_build_sparse_tree (TSTreeCursor *cursor, Lisp_Object parent,
Lisp_Object pred, Lisp_Object process_fn,
ptrdiff_t limit, Lisp_Object parser)
{
TSNode node = ts_tree_cursor_current_node (cursor);
bool match = treesit_traverse_match_predicate (node, pred, parser);
bool match = treesit_traverse_match_predicate (cursor, pred, parser, false);
if (match)
{
/* If this node matches pred, add a new node to the parent's
children list. */
TSNode node = ts_tree_cursor_current_node (cursor);
Lisp_Object lisp_node = make_treesit_node (parser, node);
if (!NILP (process_fn))
lisp_node = CALLN (Ffuncall, process_fn, lisp_node);
@ -2989,7 +3128,7 @@ a regexp. */)
if (!NILP (process_fn))
CHECK_TYPE (FUNCTIONP (process_fn), Qfunctionp, process_fn);
/* We use a default limit to 1000. See bug#59426 for the
/* We use a default limit of 1000. See bug#59426 for the
discussion. */
ptrdiff_t the_limit = 1000;
if (!NILP (limit))
@ -3000,11 +3139,12 @@ a regexp. */)
treesit_initialize ();
TSTreeCursor cursor = ts_tree_cursor_new (XTS_NODE (root)->node);
Lisp_Object parser = XTS_NODE (root)->parser;
Lisp_Object parent = Fcons (Qnil, Qnil);
TSTreeCursor cursor = treesit_cursor_helper (XTS_NODE (root)->node, parser);
treesit_build_sparse_tree (&cursor, parent, predicate, process_fn,
the_limit, parser);
ts_tree_cursor_delete (&cursor);
Fsetcdr (parent, Fnreverse (Fcdr (parent)));
if (NILP (Fcdr (parent)))
return Qnil;

View file

@ -6281,13 +6281,16 @@ static ptrdiff_t
string_buffer_position (Lisp_Object string, ptrdiff_t around_charpos)
{
const int MAX_DISTANCE = 1000;
ptrdiff_t forward_limit = min (around_charpos + MAX_DISTANCE, ZV);
ptrdiff_t found = string_buffer_position_lim (string, around_charpos,
around_charpos + MAX_DISTANCE,
false);
forward_limit, false);
if (!found)
found = string_buffer_position_lim (string, around_charpos,
around_charpos - MAX_DISTANCE, true);
{
ptrdiff_t backward_limit = max (around_charpos - MAX_DISTANCE, BEGV);
found = string_buffer_position_lim (string, around_charpos,
backward_limit, true);
}
return found;
}
@ -19429,6 +19432,13 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
blank_row (w, row, y);
goto finish_scroll_bars;
}
else if (minibuf_level >= 1)
{
/* We could have a message produced by set-minibuffer-message
displayed in the mini-window as an overlay, so resize the
mini-window if needed. */
resize_mini_window (w, false);
}
clear_glyph_matrix (w->desired_matrix);
}

View file

@ -1038,17 +1038,6 @@ unquoted file names."
(let ((default-directory nospecial-dir))
(should-error (make-directory "dir")))))
(ert-deftest files-tests-file-name-non-special-make-directory-internal ()
(files-tests--with-temp-non-special (tmpdir nospecial-dir t)
(let ((default-directory nospecial-dir))
(make-directory-internal "dir")
(should (file-directory-p "dir"))
(delete-directory "dir")))
(files-tests--with-temp-non-special-and-file-name-handler
(tmpdir nospecial-dir t)
(let ((default-directory nospecial-dir))
(should-error (make-directory-internal "dir")))))
(ert-deftest files-tests-file-name-non-special-make-nearby-temp-file ()
(let* ((default-directory (file-name-quote temporary-file-directory))
(near-tmpfile (make-nearby-temp-file "file")))
@ -1272,11 +1261,11 @@ works as expected if the default directory is quoted."
(a/b (concat dirname "a/b")))
(write-region "" nil file)
(should-error (make-directory "/"))
(should-not (make-directory "/" t))
(should (make-directory "/" t))
(should-error (make-directory dir))
(should-not (make-directory dir t))
(should (make-directory dir t))
(should-error (make-directory dirname))
(should-not (make-directory dirname t))
(should (make-directory dirname t))
(should-error (make-directory file))
(should-error (make-directory file t))
(should-not (make-directory subdir1))
@ -1357,7 +1346,9 @@ name (Bug#28412)."
(dest (concat dirname "dest/new/directory/"))
(file (concat (file-name-as-directory source) "file"))
(source2 (concat dirname "source2"))
(dest2 (concat dirname "dest/new2")))
(dest2 (concat dirname "dest/new2"))
(source3 (concat dirname "source3/d"))
(dest3 (concat dirname "dest3/d")))
(make-directory source)
(write-region "" nil file)
(copy-directory source dest t t t)
@ -1365,6 +1356,11 @@ name (Bug#28412)."
(make-directory (concat (file-name-as-directory source2) "a") t)
(copy-directory source2 dest2)
(should (file-directory-p (concat (file-name-as-directory dest2) "a")))
(make-directory source3 t)
(write-region "x\n" nil (concat (file-name-as-directory source3) "file"))
(make-directory dest3 t)
(write-region "y\n" nil (concat (file-name-as-directory dest3) "file"))
(copy-directory source3 (file-name-directory dest3) t)
(delete-directory dir 'recursive))))
(ert-deftest files-tests-abbreviate-file-name-homedir ()

View file

@ -174,6 +174,12 @@ def test2 (arg)
bar,
:a
zzz @abc,
4
foo a = 5,
b
b = $:
c = ??
@ -500,3 +506,35 @@ def resolve(**args)
member.call(**args)
end
# Endless methods.
class Bar
def foo(abc) = bar +
baz
def self.bar =
123 +
4
def foo(...) = z
def request_params = {
headers: request_headers,
body: request_body
}
def self.foo(
baz,
bar
) =
what
def foo=(
baz,
bar
)
def baz.full_name = "#{bar} 3"
baz
end
end

View file

@ -605,6 +605,18 @@ VALUES-PLIST is a list with alternating index and value elements."
(search-backward "FOO")
(should (string= (ruby-add-log-current-method) "M::C"))))
(ert-deftest ruby-add-log-current-method-after-endless-method ()
(ruby-with-temp-buffer (ruby-test-string
"module M
| class C
| def foo =
| 4_
| end
|end")
(search-backward "_")
(delete-char 1)
(should (string= (ruby-add-log-current-method) "M::C#foo"))))
(defvar ruby-block-test-example
(ruby-test-string
"class C

View file

@ -198,6 +198,141 @@
(kill-buffer base)
(kill-buffer indirect))))
;;; Tree traversal
(ert-deftest treesit-search-subtree ()
"Test `treesit-search-subtree'."
(skip-unless (treesit-language-available-p 'json))
(with-temp-buffer
(let (parser root array)
(progn
(insert "[[1,2,3], [1,2,3], [1,2,3]]")
(setq parser (treesit-parser-create 'json))
(setq root (treesit-parser-root-node parser))
(setq array (treesit-node-child root 0)))
(dolist (subarray (treesit-node-children array t))
;; Find named node forward.
(should (equal "1" (treesit-node-text
(treesit-search-subtree
subarray "number"))))
;; Find named node backward.
(should (equal "3" (treesit-node-text
(treesit-search-subtree
subarray "number" t))))
;; Find anonymous node forward.
(should (equal "[" (treesit-node-text
(treesit-search-subtree
subarray "\\[" nil t))))
;; Find anonymous node backward.
(should (equal "]" (treesit-node-text
(treesit-search-subtree
subarray "\\]" t t))))
;; If ALL=nil, it shouldn't find anonymous node.
(should (eq nil (treesit-node-text
(treesit-search-subtree
subarray "\\["))))
;; If ALL=nil, searching for number should still find the
;; numbers.
(should (equal "1" (treesit-node-text
(treesit-search-subtree
subarray "number" nil t))))
;; Find named node backward.
(should (equal "3" (treesit-node-text
(treesit-search-subtree
subarray "number" t t))))
))))
(defmacro treesit--ert-search-setup (&rest body)
"Setup macro used by `treesit-search-forward' and friends.
BODY is the test body."
`(with-temp-buffer
(let (parser root array)
(progn
(insert "[[1,2,3], [4,5,6], [7,8,9]]")
(setq parser (treesit-parser-create 'json))
(setq root (treesit-parser-root-node
parser))
(setq array (treesit-node-child root 0))
;; First bracket.
(setq cursor (treesit-node-child array 0)))
,@body)))
(ert-deftest treesit-search-forward ()
"Test `treesit-search-forward'."
(skip-unless (treesit-language-available-p 'json))
(treesit--ert-search-setup
(cl-loop for cursor = (treesit-node-child array 0)
then (treesit-search-forward cursor "" nil t)
for text in '("[" "[" "1" "," "2" "," "3" "]"
"[1,2,3]" ","
"[" "4" "," "5" "," "6" "]"
"[4,5,6]" ","
"[" "7" "," "8" "," "9" "]"
"[7,8,9]" "]"
"[[1,2,3], [4,5,6], [7,8,9]]")
while cursor
do (should (equal (treesit-node-text cursor)
text)))))
(ert-deftest treesit-search-forward-named-only ()
"Test `treesit-search-forward'."
(skip-unless (treesit-language-available-p 'json))
(treesit--ert-search-setup
(cl-loop for cursor = (treesit-node-child
(treesit-node-child array 1) 1)
then (treesit-search-forward cursor "")
for text in '("1" "2" "3" "[1,2,3]"
"4" "5" "6" "[4,5,6]"
"7" "8" "9" "[7,8,9]"
"[[1,2,3], [4,5,6], [7,8,9]]")
while cursor
do (should (equal (treesit-node-text cursor)
text)))))
(ert-deftest treesit-search-backward ()
"Test `treesit-search-forward'."
(skip-unless (treesit-language-available-p 'json))
(treesit--ert-search-setup
(cl-loop for cursor = (treesit-node-child array -1)
then (treesit-search-forward cursor "" t t)
for text in (reverse '("[[1,2,3], [4,5,6], [7,8,9]]"
"[" "[1,2,3]"
"[" "1" "," "2" "," "3" "]"
"," "[4,5,6]"
"[" "4" "," "5" "," "6" "]"
"," "[7,8,9]"
"[" "7" "," "8" "," "9" "]"
"]"))
while cursor
do (should (equal (treesit-node-text cursor)
text)))))
(ert-deftest treesit-search-backward-named-only ()
"Test `treesit-search-forward'."
(skip-unless (treesit-language-available-p 'json))
(treesit--ert-search-setup
(cl-loop for cursor = (treesit-node-child
(treesit-node-child array -1 t) -1 t)
then (treesit-search-forward cursor "" t)
for text in (reverse '("[[1,2,3], [4,5,6], [7,8,9]]"
"[1,2,3]" "1" "2" "3"
"[4,5,6]" "4" "5" "6"
"[7,8,9]" "7" "8" "9"))
while cursor
do (should (equal (treesit-node-text cursor)
text)))))
(ert-deftest treesit-cursor-helper-with-missing-node ()
"Test treesit_cursor_helper with a missing node."
(skip-unless (treesit-language-available-p 'json))
(treesit--ert-search-setup
(delete-char -1)
(setq root (treesit-buffer-root-node))
(setq array (treesit-node-child root 0))
;; If everything works, this should not hang.
(let ((missing-bracket (treesit-node-child array -1)))
(treesit-search-forward missing-bracket "" t))))
;;; Query
(ert-deftest treesit-query-api ()