mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2026-09-10 07:46:51 -04:00
Merge from origin/emacs-29
12e26cc0c1; * admin/git-bisect-start: Add mistakenly ommitted commi...9a751e0a38ruby-mode: Support endless singleton method definitions tooce7b7e5af3Remove comment-start-skip preset in tree-sitter indentati...c1e015ae32Fix recent change in tramp-smb.elcf1b771864; * etc/NEWS: Fix typos.50d18bb6eaMake tramp-archive autoloads robust for older Emacs versions3941cc29df; Improve documentation of 'setopt'6f88de109cruby-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 ...d1e0542f33Allow customising windmove user options with an empty prefixc2375e7791Improve and extend admin/git-bisect-start7cc2313eb0Make 'rmail-summary-by-thread' faster88e59b16cb; Improve documentation of installing tree-sitter and gra...897f33bf31Fix the MS-DOS build660e941235Avoid crashes in PGTK build due to signal in 'note_mouse_...0fc5fb2d05Fix MS-Windows build broken by recent treesit.c changes5b2e6d04ceFix wrong capture in typescript-ts-mode (bug#60167)cb8ccdd267Add rust-ts-mode (Bug#60136)9fcf764dd7Indentation fixes for jsx/tsx (bug#60169)69f2c71135Fix treesit-query-validate problem with view-mode1fc7535546Use cursor API in treesit-node-parent5f0286c0afSwitch to use cursor API in treesit.ca275e436dfAdd treesit_assume_true and treesit_cursor_helpera54c7a8df0Remove file-exists-in-trash-p3e02029642Update to Org 9.6-49-g47d1294a8ff671b0Don’t assume make-directory handler returns nil44c83b239dFix copy-directory bug when dest dir existsbef1edc9camake-directory now returns t if dir already exists8a9579ca29Use make-directory handlers uniformly627e7e0243Improve documentation of 'file-exists-p'c9015ef55fFix resizing of mini-windows by 'set-minibuffer-message'5a245bc786Prevent Abort dialogs from async-compiling jobs on Windows4d1e4a4893Fix 'window-max-chars-per-line' when there are no fringesd65beb820c; Revert "; * lisp/subr.el (internal--with-narrowing): Si...1c0b90e5f7ruby-mode: Recognize instance or global var as first arg ...3356c0cb16Fix end-of-defun in ruby-mode # Conflicts: # etc/NEWS
This commit is contained in:
commit
6c540e38f4
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
71
etc/NEWS.29
71
etc/NEWS.29
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.")
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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.")
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)))))
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
371
lisp/progmodes/rust-ts-mode.el
Normal file
371
lisp/progmodes/rust-ts-mode.el
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
15
src/lread.c
15
src/lread.c
|
|
@ -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 \
|
||||
|
|
|
|||
428
src/treesit.c
428
src/treesit.c
|
|
@ -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;
|
||||
|
|
|
|||
18
src/xdisp.c
18
src/xdisp.c
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ()
|
||||
|
|
|
|||
Loading…
Reference in a new issue