[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master c209802f7b: Merge from origin/emacs-29
From: |
Stefan Kangas |
Subject: |
master c209802f7b: Merge from origin/emacs-29 |
Date: |
Mon, 2 Jan 2023 08:02:37 -0500 (EST) |
branch: master
commit c209802f7b3721a1b95113290934a23fee88f678
Merge: 9377a3c889 4520f09dd8
Author: Stefan Kangas <stefankangas@gmail.com>
Commit: Stefan Kangas <stefankangas@gmail.com>
Merge from origin/emacs-29
4520f09dd8b ; * admin/git-bisect-start: Update failing commits
2569ede9c49 Update to Org 9.6-81-g563a43
d9ed736f0a7 ruby-ts-mode: Remove some currently unused functions
45618447203 ruby-ts-mode: Highlight singleton method definitions and ...
0562006da3b Add ruby-ts-mode
84e7c2fbc85 Fix fontification of C++ reference return types (bug#60441)
1864b65af60 ; Minor fix for treesit--install-language-grammar-1 (bug#...
8994f87ad40 Adjust function-call fontification in csharp-ts-mode (bug...
411647a3f65 ; Fix NEWS.
7b0b17df67e Rewrite Antinews in ELisp manual for Emacs 29
f12f72b0e09 ; * lisp/simple.el (primitive-undo): Clarify error messag...
7fd822e7f52 Update Antinews in the user manual for Emacs 29
da77d70deeb ; * test/lisp/emacs-lisp/copyright-tests.el: Fix and futu...
2baf9e107c1 Fix shortdoc-tests failure with respect to regexp-opt-cha...
5aeb8de32ee ; Fix copyright years in 2 more files.
# Conflicts:
# etc/NEWS
---
admin/git-bisect-start | 39 +-
doc/emacs/anti.texi | 192 +++---
doc/emacs/emacs.texi | 2 +-
doc/lispref/anti.texi | 225 +++----
doc/lispref/elisp.texi | 2 +-
etc/NEWS.29 | 15 +-
etc/ORG-NEWS | 2 +-
etc/images/gnus/README | 4 +-
etc/images/icons/README | 6 +-
lisp/org/ob-core.el | 7 +-
lisp/org/org-agenda.el | 13 +-
lisp/org/org-cycle.el | 2 +-
lisp/org/org-fold-core.el | 2 +-
lisp/org/org-fold.el | 2 +-
lisp/org/org-macs.el | 8 +-
lisp/org/org-persist.el | 30 +-
lisp/org/org-src.el | 154 ++---
lisp/org/org-version.el | 2 +-
lisp/progmodes/c-ts-mode.el | 2 +-
lisp/progmodes/csharp-mode.el | 64 +-
lisp/progmodes/eglot.el | 2 +-
lisp/progmodes/ruby-ts-mode.el | 944 ++++++++++++++++++++++++++++++
lisp/simple.el | 2 +-
lisp/treesit.el | 2 +
test/lisp/emacs-lisp/copyright-tests.el | 6 +-
test/lisp/emacs-lisp/shortdoc-tests.el | 1 +
test/lisp/progmodes/ruby-ts-mode-tests.el | 254 ++++++++
27 files changed, 1636 insertions(+), 348 deletions(-)
diff --git a/admin/git-bisect-start b/admin/git-bisect-start
index 6015b6b291..a439ee7fe1 100755
--- a/admin/git-bisect-start
+++ b/admin/git-bisect-start
@@ -82,7 +82,7 @@ done
# SKIP-BRANCH 58cc931e92ece70c3e64131ee12a799d65409100
## The list below is the exhaustive list of all commits between Dec 1
-## 2016 and Nov 30 2022 on which building Emacs with the default
+## 2016 and Dec 31 2022 on which building Emacs with the default
## options, on a GNU/Linux computer and with GCC, fails. It is
## possible (though unlikely) that building Emacs with non-default
## options, with other compilers, or on other platforms, would succeed
@@ -1637,3 +1637,40 @@ $REAL_GIT bisect skip $(cat $0 | grep '^# SKIP-SINGLE '
| sed 's/^# SKIP-SINGLE
# SKIP-SINGLE 4552b01d8c8052f607dca2fcddcf7b2e270f1db6
# SKIP-SINGLE b6568c1389128d47538b646d940427949ddf58d0
# SKIP-SINGLE 6d5b34d9de7b2f1b346d9aff123ad20c942166dc
+# SKIP-SINGLE d9d90666f545dc25be63c1b16c030ce1aa96510e
+# SKIP-SINGLE e645bcc26d468ab6b8e16b6160c203c5db70ec6b
+# SKIP-SINGLE 0aea1cf8190aa804a0d11a67b4a3cb4b715ae82d
+# SKIP-SINGLE 7e6d1d1c47196bf1bb5254f5c9014e25bdaf9833
+# SKIP-SINGLE 784e509bded0fe41dd9908022a92c54ac8c21a2c
+# SKIP-SINGLE bc4cbbcc57a56a23c64576c8c23ecf6afb1c747b
+# SKIP-SINGLE 523261b454058d0b28df2c3de1eab55fe378aa69
+# SKIP-SINGLE 29d23b7fa00ed8263baa060d487b526d51fa6986
+# SKIP-SINGLE 9371d488be62a37788b499a7e44b1f5db158e212
+# SKIP-SINGLE 60418e6f09c67924e3e05eb4948e109d8f7c4073
+# SKIP-SINGLE 9153cf8158489d387a6a0d9d0ede9a2528c35f0a
+# SKIP-SINGLE d11e34ce76aac8680337f247419657e042e4cf34
+# SKIP-SINGLE 2541bec21bf3cf090071e434dac170d52394594e
+# SKIP-SINGLE 007e66bccb2cb8382158e5e24727fd1b4478cd69
+# SKIP-SINGLE 753b7a1cff6b8ce2367a94d27b615ac31f1067ba
+# SKIP-SINGLE 7c63b632e4e2241a28f08015cc981a72e18d7867
+# SKIP-SINGLE 91ae9f3d12885373d38c3e8d693f7dc210f9d471
+# SKIP-SINGLE 314cbef84944145e2160736ce32812403ed99cd9
+# SKIP-SINGLE 1a88a28ace24c8b4fb1e4780948b50dd37ada539
+# SKIP-SINGLE 98327e371938033f7ccefd1c5226cd102cb29ad1
+# SKIP-SINGLE 9d814bea4600ac28dcdbf9caf386467551d7d9be
+# SKIP-SINGLE 73769dc2b872441eb0b8565e1090e97fc0b5d521
+# SKIP-SINGLE 283043621756fd004906ecdd5ba829a47cb3fc57
+# SKIP-SINGLE 05ece1eb8b7ce28d366d02df89449d453be8d37e
+# SKIP-SINGLE 248c13dcfe1b9618811a6fe67e967b25b1a8f139
+# SKIP-SINGLE 38c35bf0f6a938001dfecbe439addf8fb62897c6
+# SKIP-SINGLE 9065d745151e1995b80a1f4d5a04e2af111ad928
+# SKIP-SINGLE e78e69b33189c653d1588b810283969ac3cca137
+# SKIP-SINGLE 909091d7578b7225601b202fb9257dedae879e9a
+# SKIP-SINGLE 706ed85285515e7047e16608815c1d02d4907b07
+# SKIP-SINGLE 7013b0179cbe5cce19e114d7673770d1425d3005
+# SKIP-SINGLE 2de25accaf31aef643557ec476041c770fc7ac15
+# SKIP-SINGLE 2b1fdbffcb595bcd72fa9aa3db674c6985042bcb
+# SKIP-SINGLE 1480865e641b06d570f5ab56011f8e3e5481da7d
+# SKIP-SINGLE 8c13e8497821881b5197a1717e9e53b9991859d0
+# SKIP-SINGLE a6db8464e150c49724c71c5969b97f205ee2dec5
+# SKIP-SINGLE cfbfd393b450d4eb7ac0b7922b44208688553c9e
diff --git a/doc/emacs/anti.texi b/doc/emacs/anti.texi
index 58ab06b36d..c46110a530 100644
--- a/doc/emacs/anti.texi
+++ b/doc/emacs/anti.texi
@@ -4,134 +4,154 @@
@c See file emacs.texi for copying conditions.
@node Antinews
-@appendix Emacs 27 Antinews
+@appendix Emacs 28 Antinews
@c Update the emacs.texi Antinews menu entry with the above version number.
For those users who live backwards in time, here is information
-about downgrading to Emacs version 27.2. We hope you will enjoy the
+about downgrading to Emacs version 28.2. We hope you will enjoy the
greater simplicity that results from the absence of many @w{Emacs
@value{EMACSVER}} features.
@itemize @bullet
@item
-Emacs can no longer be built with support of native compilation of
-Lisp programs. This means Emacs builds much faster, and the problems
-that came with native compilation: the need to have GCC and Binutils
-installed, the complications of managing your @file{eln-cache}
-directories---all of that is now future history. The simplicity and
-elegance of the Emacs byte-compiled code is now restored in all of its
-pristine beauty.
+Like its newer releases, Emacs 28 can still be built with support of
+native compilation of Lisp programs. However, in preparation for
+removal of this feature in some previous version, we've deleted the
+capability of ahead-of-time native compilation of all the Lisp files
+that come with Emacs. This makes the Emacs build process much faster.
@item
-Emacs no longer builds by default with Cairo, even if it's present.
-The warnings about not using HarfBuzz are also gone, in preparation
-for complete removal of HarfBuzz support in previous Emacs versions.
-Fancy text shaping and display is becoming less important as you move
-back in time. The @code{ftx} font backend is again part of Emacs, for
-the same reasons.
+Emacs can no longer be built with the tree-sitter library, so you no
+longer will need to look for and install the grammar libraries for
+the languages in which you want to program. Similarly, all the modes
+that are based on the tree-sitter library were deleted, leaving you
+with just one major mode for every supported programming language: no
+more need to decide whether to turn the tree-sitter supported modes on
+and try using their parser-based fontification, indentation, and other
+features. For some languages and file types, this means no major mode
+at all, leaving you with the venerable Fundamental mode as the
+natural, high-performance choice. For example, Go, Rust, and CMake
+files no longer have any major modes for editing their files ---
+another milestone towards a simpler, leaner Emacs.
@item
-Emacs once again supports versions 5.3 and older OpenBSD systems,
-which will be needed as you move back in time.
+Built-in support for accessing SQLite databases was removed. You can
+now again edit SQLite files as simple binary files, which Emacs is
+quite capable to support, as it always did.
@item
-We've dropped support for Secure Computing filter on GNU/Linux. The
-past world is much more secure than the present, so the complexities
-related with this stuff, which can only be explained by severe
-paranoia, are no longer justified.
+As a gesture to users of the Haiku operating system, we've dropped the
+code which allowed Emacs to be built on that OS@. We expect Haiku
+users to enjoy the much simpler editors they have for editing their
+files.
@item
-Emacs reverted back to supporting Unicode 13.x, since the following
-versions of the standards are not yet published where you are going.
-The @samp{emoji} script and the support for displaying Emoji sequences
-were removed for the same reasons: no one will produce them in the
-past.
+Support for XInput2 input events on X is gone. We think the
+traditional X input events are more than enough, certainly so as you
+move back in time, where XInput2 will eventually be removed from X as
+well, once the maintainers of the X Windows system realize the utter
+futility of supporting fancy input mechanisms.
@item
-Mode-specific commands and the @kbd{M-S-x} command that invokes them
-were removed. As you move back in time, the command set in Emacs
-becomes smaller, so any such filtering of applicable commands just
-gets in the way.
+The ``pure GTK'' (a.k.a.@: @acronym{PGTK}) configuration of Emacs is
+no longer supported. This is in anticipation of the complete removal
+of the GTK toolkit support from Emacs, and in accordance with our
+expectation that GTK will cease to exist as you move back in time. We
+plan on removing support for all the other toolkits as well, leaving
+only the pure X build with our own widgets as the single supported GUI
+configuration on X.
@item
-We have removed the system for displaying documentation of groups of
-related functions, the @kbd{shortdoc-display-group} command to go with
-it, and the corresponding ``See also'' button in the @file{*Help*}
-buffer. That should make searching for certain functions simpler:
-just use the venerable @samp{apropos} commands.
+The @option{--init-directory} command-line option was removed, as
+initializing Emacs with init files of another user is a preposterous
+idea anyway.
@item
-The @code{context-menu-mode} was removed, and with it the context
-menus popped by pressing the right mouse button. This is one small
-step towards freeing Emacs (and eventually, the whole world of
-computing) from the tyranny of the GUI pointing devices in general,
-and moving back to the simplicity of text-mode user interfaces.
-Down with mice and other rodents!
+In line with simplifying and eventually removing the
+native-compilation option, we've deleted the
+@code{inhibit-automatic-native-compilation} variable and its support
+code. This greatly simplifies how native compilation works and makes
+your configure-time decision regarding native compilation in Emacs
+clear-cut: either Emacs always compiles Lisp to native code before
+using it, or it never does so; no more half measures and special
+exceptions. For similar reasons, @code{native-compile-prune-cache}
+and @code{startup-redirect-eln-cache} features are no longer part of
+Emacs.
@item
-The commands @kbd{C-x 4 4} and @kbd{C-x 5 5} for displaying the
-results in a new window/frame re gone. We are quite certain that
-creating a new window/frame before running a command is much simpler,
-and doesn't require a complication of a new prefix.
+We've deleted the special code and features which allowed Emacs to
+present decent performance and responsiveness when editing files with
+very long lines. Such files become more and more rare as time goes
+back, and so having all this tricky code in Emacs for their benefit
+was deemed an unnecessary complication.
@item
-The behavior of active minibuffers when switching frames is now the
-perfect mess it should be: sometimes the minibuffer moves to the new
-selected frame, sometimes it doesn't, and sometimes you get an error.
-This makes Emacs usage much more fun, as you get to guess the result,
-instead of having it boringly consistent.
+Emacs dropped support for Eglot and the LSP servers. We decided that
+the built-in ways of analyzing source code are more than enough as you
+move back in time.
@item
-Compact mode-line display mode has been removed. The items displayed
-on the mode line are now always in the same place, and if there's not
-enough space for them, they are not displayed at all, instead of being
-confusingly displayed in a different position. You no longer need to
-think twice where to find a particular mode-line element on display.
+Commands to scale and rotate images are once again bound to single
+keys like @kbd{+}, @kbd{-}, and @kbd{r}, which makes them much easier
+to type. As for the risk of typing these by mistake, we don't believe
+Emacs users make typing mistakes, especially as they move back in
+time and become younger and younger.
@item
-Many commands and options related to tab bars were removed, including
-(but not limited to) frame-specific appearance of tab bars, the
-@code{tab-bar-format} option, the @kbd{C-x t n}, @kbd{C-x t N},
-@kbd{C-x t M}, and @kbd{C-x t G} commands, and many mouse gestures on
-the tab bar. We are going to delete the tab bar support from Emacs in
-one of the past versions, and this is a step in that direction.
+To simplify typing popular commands, we've rebound the @w{@kbd{C-x 8 . .}}
+back to @w{@kbd{C-x 8 .}} and @w{@kbd{C-x 8 = =}} back to @w{@kbd{C-x 8 =}}.
+There's no need for fancier, longer key sequences, as moving back in
+time means we will have fewer and fewer commands to bind to them in
+the first place.
@item
-The ``transient'' input methods have been removed; use @kbd{C-\} to
-turn input methods on and off instead. This is in preparation for
-complete removal of input methods from Emacs in version 19, and
-consistent with the fact that the number of input methods we support
-becomes smaller as you move back in time.
+If you inadvertently kill the @file{*scratch*} buffer, Emacs will
+recreate it in Fundamental mode, not in Lisp Interaction mode. You
+get to turn on the mode you like yourself. Our long-term plans for
+past Emacs releases is to remove the recreation of @file{*scratch*}
+altogether, and this is the first step in that direction.
@item
-We disabled @code{show-paren-mode} by default, since we think the
-venerable @code{blink-matching-paren} feature is more than enough, and
-better fits the simplicity of past Emacs versions. It will definitely
-be better when colors are removed from Emacs in the distant past.
+Support for @code{rlogin} and @code{rsh} protocols are back, since we
+expect them to become more and more important and popular as you move
+back in time.
-For the same reason, sub-groups in interactive regexp searches are no
-longer highlighted in distinct colors.
+@item
+In preparation for eventual removal of Unicode support from Emacs,
+we've downgraded our Unicode support to version 14.0.
+
+@item
+You can no longer change the size of the font globally. Since Emacs
+will at some past date remove all support for variable-size fonts,
+having such commands is a luxury we are better without.
+
+@item
+On our permanent quest for simplifying Emacs, we've removed the
+commands @code{duplicate-line} and @code{duplicate-dwim}; the old-time
+friends @kbd{M-w} and @kbd{C-y} (typed one or more times) should
+suffice. The command @code{rename-visited-file} is gone for the same
+reason.
@item
-On our permanent quest for simplifying Emacs, we've removed the Ispell
-command @code{ispell-comment-or-string-at-point}; the old-time friend
-@code{ispell-comments-and-strings} should suffice.
+We've deleted many commands related to Emoji, which were bound in the
+@kbd{C-x 8 e} prefix keymap. We decided that the ability to type
+Emoji sequences using @kbd{C-x 8 @key{RET}} is enough, and actually
+serves our users better by requiring them to know the codepoints of
+the sequences they want to type.
@item
-Many Gnus commands and options were deemed to unnecessarily complicate
-the use of Gnus (which is too complex to begin with), and thus were
-removed. This includes @code{gnus-topic-display-predicate},
-@code{gnus-process-mark-toggle}, @code{gnus-registry-register-all},
-@code{gnus-paging-select-next}, and many others. The @code{nnselect}
-backend was deleted for the same reason.
+We dropped support for many scripts and input methods, especially old
+scripts that no one uses anyway. For similar reasons, Greek and
+Ukrainian translations of the Emacs tutorial are not available
+anymore.
@item
-The @file{project.el} package have been redesigned to remove many
-unnecessary features, so that just the bare essentials remain. We
-plan on removing this package from Emacs in a previous version, but
-decided to begin with removing some extra features first.
+@file{package.el} can no longer fetch source code of packages from
+their VCS repositories. We think command-line tools like Git should
+be enough to allow you to clone their repositories. So we deleted
+the @code{package-vc-install} command and other similar commands.
@item
To keep up with decreasing computer memory capacity and disk space, many
-other functions and files have been eliminated in Emacs 27.2.
+other functions and files have been eliminated in Emacs 28.2.
@end itemize
diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi
index 83486a2379..b6d149eb3e 100644
--- a/doc/emacs/emacs.texi
+++ b/doc/emacs/emacs.texi
@@ -220,7 +220,7 @@ Appendices
* GNU Free Documentation License:: The license for this documentation.
* Emacs Invocation:: Hairy startup options.
* X Resources:: X resources for customizing Emacs.
-* Antinews:: Information about Emacs version 27.
+* Antinews:: Information about Emacs version 28.
* Mac OS / GNUstep:: Using Emacs under macOS and GNUstep.
* Haiku:: Using Emacs on Haiku.
* Microsoft Windows:: Using Emacs on Microsoft Windows and MS-DOS.
diff --git a/doc/lispref/anti.texi b/doc/lispref/anti.texi
index 157a7f8cc8..cddf675d33 100644
--- a/doc/lispref/anti.texi
+++ b/doc/lispref/anti.texi
@@ -6,179 +6,182 @@
@c This node must have no pointers.
@node Antinews
-@appendix Emacs 27 Antinews
+@appendix Emacs 28 Antinews
@c Update the elisp.texi Antinews menu entry with the above version number.
For those users who live backwards in time, here is information about
-downgrading to Emacs version 27.2. We hope you will enjoy the greater
+downgrading to Emacs version 28.2. We hope you will enjoy the greater
simplicity that results from the absence of many @w{Emacs
@value{EMACSVER}} features.
@itemize @bullet
@item
-The annoying @code{lexical-binding} local variable now heeds the
-value of @code{enable-local-variables}: if it's @code{nil}, the
-@code{lexical-binding} cookie is ignored. We are working hard on
-removing the lexical-binding support in some past Emacs version, and
-this small step advances us back to that change.
+The implementation of overlays is back to its simple, time-proven
+storage in a pair of linear linked lists centered around some buffer
+position. No more fancy interval trees and suchlikes. Lisp programs
+that use overlays once again need to recenter overlays around the
+buffer position of interest, and display-related features should again
+make sure they don't use too many overlays in a buffer, lest redisplay
+will be too slow.
@item
-The @code{load-dangerous-libraries} variable is not obsolete, as it
-must be used to allow loading Lisp compiled by XEmacs, which will
-become more and more important as you move back in time.
+Several functions stopped the annoying conversion of quotes and key
+sequences by no longer calling @code{substitute-command-keys}. One
+prominent example is @code{format-prompt} and all its many callers.
+This makes the strings they produce much more predictable, returning
+to you, the Lisp programmer, control on which punctuation characters
+will appear in the text presented to the users. For similar reasons,
+the @code{substitute-quotes} function was deleted.
@item
-The optional @var{modes} argument of @code{interactive} is not
-supported, and every command is deemed applicable to any major mode.
-We believe this makes the life of Lisp programmers much simpler, as
-there's now no need to tag commands with the modes where they make
-sense.
+The venerable @code{buffer-modified-p} function again reliably returns
+either @code{nil} or @code{t}, not any other confusing values.
@item
-Shorthands for Lisp symbols have been removed, which makes loading
-Lisp files and handling Lisp symbols much simpler and more efficient.
-This is important for decent performance on slower CPUs as you move
-back in time.
+The support for @samp{medium} weight of fonts was dropped. Emacs now
+considers @samp{medium} and @samp{regular} weights to be the same. We
+believe this will simplify your font setup, since there's no longer a
+need to worry about fonts that support @samp{regular} weight, but not
+the @samp{medium} one, or vice versa: either one will do!
@item
To reduce the amount of code in Emacs related to unimportant features,
-we've removed the variables @code{global-minor-modes} and
-@code{local-minor-modes}. If your Lisp program needs to determine
-whether some minor mode is in effect, it will have to test explicitly
-for every mode. We don't expect anyone to miss those fancy variables.
+we've removed the function @code{compiled-function-p}. Lisp programs
+are expected to test explicitly for the relevant types of function
+objects: built-in, byte-compiled, and natively-compiled. For the same
+reasons we deleted the functions @code{pos-bol}, @code{pos-eol},
+@code{file-attribute-file-identifier}, and quite a few others. We
+don't expect anyone to miss those fancy functions.
@item
-The default preference for servicing sub-processes that produce output
-at a high rate, and the associated variable
-@code{process-prioritize-lower-fds}, have been removed. Moving back
-in time means fewer and fewer programs can produce such high-rate
-output, so this features becomes just useless crud.
+The timeout used by @code{x-show-tip} can no longer be specified by
+Lisp programs; it is hard-coded in the function. This will lead to a
+simpler, easier maintained code, and no one should want to control the
+timeout after which the tip pops down.
@item
-The encodings that are variants of EBCDIC were removed. This includes
-@code{ibm256}, @code{ibm273}, and others---variants of the EBCDIC
-encoding tailored for some Japanese and European locales. You won't
-need those where you are going.
+The macro @code{setopt} was deleted; use @code{customize-variable}
+instead, or invoke the @code{:set} function from Lisp.
@item
-The ``Bindat type expression'' description language has been removed,
-as the existing data layout specifications are perfectly suited for
-this job.
+We removed the @code{lisp-directory} variable, as the value can be
+easily deduced from other similar variables, like
+@code{installation-directory} and @code{source-directory}, each one
+when it's relevant.
@item
-To simplify code and reduce complexity, we removed the capability of
-specifying the success handler in @code{condition-case} via the
-@code{:success} keyword. If you really need this feature (why would
-you?), you can always write some simple Lisp that has the same effect.
+To simplify code and reduce complexity, we deleted the functions
+@code{get-display-property} and @code{add-display-text-property}; use
+the generic @code{get-text-property} and @code{put-text-property}
+instead.
@item
-Emacs modules can no longer provide interactive functions, or install
-finalizers, nor open channels to existing pipe sub-processes. All
-this is extra ballast, especially since we plan on removing modules in
-some past Emacs version. The @code{make_unibyte_string} module API
-was removed for the same reason.
+Support for pinch input events and for modern drag-and-drop
+functionality on X was dropped. As you move back in time, these
+facilities will become less and less important, and will soon enough
+disappear, so there's no reason to keep them in Emacs.
@item
-To keep Emacs clean and elegant, we've removed the
-@code{print-integers-as-characters} option. Recognizing characters by
-their decimal codes is a basic requirement for Emacs Lisp programmers,
-and with the expected decrease in use of Unicode characters, this will
-be soon limited to ASCII only: surely something you all can master!
+To keep Emacs clean and elegant, we've removed the @file{textsec.el}
+library, with its facilities for checking whether some text is
+``suspicious''. We consider our users smart enough to detect
+maliciously modified text by just looking at it or by moving the
+cursor across it, and the whole idea that someone would wish to
+deliberately deceive Emacs users ridiculous and unworthy of
+complicating our elegant text-processing and display capabilities.
@item
-The optional @var{count} argument of the @code{directory-files}
-function has been removed. Extracting the first @var{n} members from
-the full list is trivial, so this is a significant simplification for
-an insignificant cost.
+The functions @code{keymap-set}, @code{keymap-global-set},
+@code{keymap-local-set}, @code{keymap-substitute},
+@code{keymap-lookup}, and some others were deleted. We have found the
+traditional @code{define-key}, @code{global-set-key},
+@code{local-set-key}, @code{substitute-key-definition}, and
+@code{key-binding} more than enough, and their minor inconsistencies
+in the syntax of keys they accept a source of endless fun in Emacs
+Lisp programming. Why make Emacs programming a dull place? For the
+same reasons we deleted @code{key-valid-p}, since we consider the
+permissive nature of @code{kbd} more in the spirit of Emacs Lisp.
@item
-Functions that create sub-processes and network connections no longer
-accept the @code{:coding} argument; use
-@code{set-process-coding-system} or bind
-@code{coding-system-for-read/write} instead: again, a significant
-reduction in Emacs complexity for little or no cost.
+Yanking of anything but plain text from other applications becomes
+more and more an unnecessary feature as you move back in time, so we
+dropped support for pasting media like HTML and images via the
+clipboard. If you @i{really} need to yank those into an Emacs buffer,
+you can go via a disk file.
@item
-We deleted from the macros @code{define-derived-mode} and
-@code{define-minor-mode} the code which allowed using the
-@code{:interactive} argument. The possibility of marking a mode
-non-interactive makes very little sense,
+We removed unnecessary functions @code{string-pixel-width} and
+@code{string-glyph-split}, as we consider it inappropriate for Lisp
+programs to do display layout calculations, where these functions come
+in handy. Display is for the display engine, written in C, and should
+stay there!
@item
-The possibility of having links to man pages in doc strings has been
-removed. Use plain text instead, if you need such references.
+Various new Xwidget functions, such as
+@code{xwidget-perform-lispy-event}, @code{xwidget-webkit-load-html},
+and @code{xwidget-webkit-back-forward-list}, were deleted as part of
+our continuing effort to gradually delete the entire Xwidget
+functionality in some previous release of Emacs.
@item
-Temporary buffers are no longer exempt from running any buffer-related
-hooks. Programs that don't want such hooks in some buffer can always
-disable it locally, whereas making that simpler complicates Emacs for
-no good reason.
+Setting the @code{:stderr} property of a process in a
+@code{make-process} call once again forces the process's connection to
+use pipes, not ptys, for all the standard streams --- a considerable
+simplification of this complex interface.
@item
+To keep the amount of Lisp functions from growing out of control, we
+deleted @code{string-equal-ignore-case}. Use @code{compare-strings}
+instead.
+
Several features that complicated the byte compiler have been removed:
@itemize @minus
@item
-The checks for missing declarations of dynamic variables. This will
-continue making less and less sense as we move away of lexical-binding
-support.
-
-@item
-The ability of compiling symlinked @file{*.el} files, which is really
-gross: copy the files instead.
+The warnings about quoting mistakes in documentation strings. You are
+expected to find such mistakes yourself, by eyeballing the resulting
+@file{*Help*} buffer display.
@item
-The warnings about too-wide doc strings---that is just a nuisance, as
-the programmers should be trusted to know what they are doing.
+The warnings about malformed @code{defcustom} types, like
+double-quoting symbols in @code{choice} lists.
@end itemize
-
-@item
-We deleted several features of the @code{pcase} macro, in accordance
-with our general plan to remove @code{pcase} from Emacs:
-
-@itemize @minus
-@item
-The @code{cl-type} pattern.
-
@item
-the @code{pcase-setq} macro.
-
-@item
-The @code{pcase-compile-patterns} function.
-@end itemize
+The macro @code{with-buffer-unmodified-if-unchanged} was deleted.
+Lisp programs that need to leave the buffer unmodified in these cases
+can always compare the text before and after the modifications.
@item
-Some of the keywords used in Edebug specification lists were deemed to
-be of little use, and were therefore removed: @code{&interpose},
-@code{&error}, and @code{&name}. The long-term plane is for Emacs to
-drop Edebug entirely, leaving only the trusted Lisp debugger, and we
-continue working according to that plan.
+The functions @code{string-edit} and @code{read-string-from-buffer}
+were removed, as we consider the fun of programming them anew every
+time an important part of the education of each Emacs Lisp developer.
@item
-The function @code{object-intervals} was dropped, as a Lisp program
-can easily collect the intervals of a buffer or a string by iterating
-through them one by one.
+We deleted the function @code{readablep} and the related variable
+@code{print-unreadable-function}, since no one is supposed to want to
+print unreadable Lisp objects.
@item
-We decided that the @code{require-theme} function is an unnecessary
-complication, so we deleted it. Lisp programs can easily search along
-@code{custom-theme-load-path} instead.
+The facility for storing multisession variables was deleted as an
+unnecessary complication. With it are gone @code{multisession-value},
+@code{define-multisession-variable}, and
+@code{list-multisession-values}.
@item
-The convenience functions @code{length<}, @code{length>}, and
-@code{length=} were removed, as using @code{length} followed by a
-comparison should be good enough for everyone, especially considering
-that the typical length of a list keeps going down as you move back
-through time.
+The support for the @code{cursor-face} text property was dropped. We
+consider the rest of the faces adequate for supporting this
+functionality.
@item
-The variable @code{current-minibuffer-command} is no longer available,
-as we found little justification for keeping it.
+The function @code{tooltip-show} dropped support for optional face
+arguments @code{text-face} and @code{default-face} that allow fancy
+control of the face of the tip text and top frame colors. We decided
+that tooltips should all look the same, to prevent user confusion.
@item
As part of the ongoing quest for simplicity, many other functions and
variables have been eliminated. Other functions and variables, that
-were declared obsolete since Emacs 23, have been added back, in
-preparation for releasing Emacs 23 in some distant past.
+were declared obsolete since Emacs 24, have been added back, in
+preparation for releasing Emacs 24 in some distant past.
@end itemize
diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi
index cbc4c37824..fd06409fd5 100644
--- a/doc/lispref/elisp.texi
+++ b/doc/lispref/elisp.texi
@@ -235,7 +235,7 @@ To view this manual in other formats, click
Appendices
-* Antinews:: Info for users downgrading to Emacs 27.
+* Antinews:: Info for users downgrading to Emacs 28.
* GNU Free Documentation License:: The license for this documentation.
* GPL:: Conditions for copying and changing GNU Emacs.
* Tips:: Advice and coding conventions for Emacs Lisp.
diff --git a/etc/NEWS.29 b/etc/NEWS.29
index ae3c06c487..355ba6ba8a 100644
--- a/etc/NEWS.29
+++ b/etc/NEWS.29
@@ -3261,6 +3261,11 @@ written in YAML. It is auto-enabled for files with the
".yaml" or
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.
+---
+*** New major mode 'ruby-ts-mode'.
+An optional major mode based on the tree-sitter library for editing
+programs in the Ruby language.
+
* Incompatible Lisp Changes in Emacs 29.1
@@ -3812,21 +3817,21 @@ Standard.
** seq
+++
-** New function 'seq-split'.
+*** New function 'seq-split'.
This returns a list of sub-sequences of the specified sequence.
+++
-** New function 'seq-remove-at-position'.
+*** New function 'seq-remove-at-position'.
This function returns a copy of the specified sequence where the
element at a given (zero-based) index got removed.
+++
-** New function 'seq-positions'.
+*** New function 'seq-positions'.
This returns a list of the (zero-based) indices of elements matching a
given predicate in the specified sequence.
+++
-** New function 'seq-keep'.
+*** New function 'seq-keep'.
This is like 'seq-map', but removes all nil results from the returned
list.
@@ -4108,7 +4113,7 @@ where 'major-mode' is 'shell-mode' or a combined with a
condition like
+++
** New function 'match-buffers'.
-Use 'buffer-match-p' to gather a list of buffers that match a
+It uses 'buffer-match-p' to gather a list of buffers that match a
condition.
---
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 62c918564d..d610a63b09 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -24,7 +24,7 @@ the time being. It will be removed in the next release.
See https://orgmode.org/list/87r0yk7bx8.fsf@localhost for more details.
-*** Element cache is enabled by default and work for headings
+*** Element cache is enabled by default and works for headings
The old element cache code has been refactored. Emacs does not hang
anymore when the cache is enabled.
diff --git a/etc/images/gnus/README b/etc/images/gnus/README
index 7fe27efc80..f05cf0f7f1 100644
--- a/etc/images/gnus/README
+++ b/etc/images/gnus/README
@@ -21,11 +21,11 @@ Files: catchup.pbm catchup.xpm cu-exit.pbm cu-exit.xpm
unsubscribe.pbm unsubscribe.xpm uu-decode.pbm uu-decode.xpm
uu-post.pbm uu-post.xpm
Author: Luis Fernandes <elf@ee.ryerson.ca>
-Copyright (C) 2001-2022 Free Software Foundation, Inc.
+Copyright (C) 2001-2023 Free Software Foundation, Inc.
Files: gnus.png, gnus.svg
Author: Francesc Rocher <rocher@member.fsf.org>
- Copyright (C) 2008-2022 Free Software Foundation, Inc.
+ Copyright (C) 2008-2023 Free Software Foundation, Inc.
* The following icons are from GNOME 2.x. They are not part of Emacs,
diff --git a/etc/images/icons/README b/etc/images/icons/README
index d6065232e0..bb455980ca 100644
--- a/etc/images/icons/README
+++ b/etc/images/icons/README
@@ -14,7 +14,7 @@ Files: hicolor/16x16/apps/emacs23.png
hicolor/24x24/apps/emacs23.png
hicolor/128x128/apps/emacs23.png hicolor/scalable/apps/emacs23.svg
Author: Kentaro Ohkouchi <nanasess@fsm.ne.jp>
-Copyright (C) 2007-2022 Free Software Foundation, Inc.
+Copyright (C) 2007-2023 Free Software Foundation, Inc.
License: GNU General Public License version 3 or later (see COPYING)
@@ -22,7 +22,7 @@ Files: hicolor/16x16/apps/emacs22.png
hicolor/24x24/apps/emacs22.png
hicolor/32x32/apps/emacs22.png hicolor/48x48/apps/emacs22.png
Author: Andrew Zhilin <andrew_zhilin@yahoo.com>
-Copyright (C) 2005-2022 Free Software Foundation, Inc.
+Copyright (C) 2005-2023 Free Software Foundation, Inc.
License: GNU General Public License version 3 or later (see COPYING)
Files: allout-widgets-dark-bg/closed.png
@@ -71,5 +71,5 @@ Files: allout-widgets-dark-bg/closed.png
allout-widgets-light-bg/through-descender.xpm
Author: Ken Manheimer <ken.manheimer@gmail.com>
-Copyright (C) 2011-2022 Free Software Foundation, Inc.
+Copyright (C) 2011-2023 Free Software Foundation, Inc.
License: GNU General Public License version 3 or later (see COPYING)
diff --git a/lisp/org/ob-core.el b/lisp/org/ob-core.el
index dacf420bd1..9bb77f7920 100644
--- a/lisp/org/ob-core.el
+++ b/lisp/org/ob-core.el
@@ -2461,13 +2461,18 @@ INFO may provide the values of these header arguments
(in the
(insert
(org-trim
(org-list-to-org
+ ;; We arbitrarily choose to format non-strings
+ ;; as %S.
(cons 'unordered
(mapcar
(lambda (e)
(cond
((stringp e) (list e))
((listp e)
- (mapcar (lambda (x) (format "%S" x)) e))
+ (mapcar
+ (lambda (x)
+ (if (stringp x) x (format "%S" x)))
+ e))
(t (list (format "%S" e)))))
(if (listp result) result
(split-string result "\n" t))))
diff --git a/lisp/org/org-agenda.el b/lisp/org/org-agenda.el
index 3376dbc114..66b08adf53 100644
--- a/lisp/org/org-agenda.el
+++ b/lisp/org/org-agenda.el
@@ -3477,14 +3477,14 @@ This ensures the export commands can easily use it."
(setq props (plist-put props 'tags (mapconcat #'identity tmp ":"))))
(when (setq tmp (plist-get props 'date))
(when (integerp tmp) (setq tmp (calendar-gregorian-from-absolute tmp)))
- (let ((calendar-date-display-form '(year "-" month "-" day)))
- '((format "%4d, %9s %2s, %4s" dayname monthname day year))
-
+ (let ((calendar-date-display-form
+ '(year "-" (string-pad month 2 ?0 'left) "-" (string-pad day 2 ?0
'left))))
(setq tmp (calendar-date-string tmp)))
(setq props (plist-put props 'date tmp)))
(when (setq tmp (plist-get props 'day))
(when (integerp tmp) (setq tmp (calendar-gregorian-from-absolute tmp)))
- (let ((calendar-date-display-form '(year "-" month "-" day)))
+ (let ((calendar-date-display-form
+ '(year "-" (string-pad month 2 ?0 'left) "-" (string-pad day 2 ?0
'left))))
(setq tmp (calendar-date-string tmp)))
(setq props (plist-put props 'day tmp))
(setq props (plist-put props 'agenda-day tmp)))
@@ -4678,7 +4678,7 @@ is active."
(org-agenda-text-search-extra-files org-agenda-text-search-extra-files)
regexp rtn rtnall files file pos inherited-tags
marker category level tags c neg re boolean
- ee txt beg end words regexps+ regexps- hdl-only buffer beg1 str)
+ ee txt beg end last-search-end words regexps+ regexps- hdl-only buffer
beg1 str)
(unless (and (not edit-at)
(stringp string)
(string-match "\\S-" string))
@@ -4817,6 +4817,7 @@ is active."
(throw 'nextfile t))
(goto-char (max (point-min) (1- (point))))
(while (re-search-forward regexp nil t)
+ (setq last-search-end (point))
(org-back-to-heading t)
(while (and (not (zerop
org-agenda-search-view-max-outline-level))
(> (org-reduced-level (org-outline-level))
@@ -4878,7 +4879,7 @@ is active."
'priority 1000
'type "search")
(push txt ee)
- (goto-char (1- end))))))))))
+ (goto-char (max (1- end) last-search-end))))))))))
(setq rtn (nreverse ee))
(setq rtnall (append rtnall rtn)))
(org-agenda--insert-overriding-header
diff --git a/lisp/org/org-cycle.el b/lisp/org/org-cycle.el
index f4d84f5058..828c84cd0a 100644
--- a/lisp/org/org-cycle.el
+++ b/lisp/org/org-cycle.el
@@ -1,6 +1,6 @@
;;; org-cycle.el --- Visibility cycling of Org entries -*- lexical-binding: t;
-*-
;;
-;; Copyright (C) 2020, 2023-2020 Free Software Foundation, Inc.
+;; Copyright (C) 2020-2023 Free Software Foundation, Inc.
;;
;; Maintainer: Ihor Radchenko <yantar92 at gmail dot com>
;; Keywords: folding, visibility cycling, invisible text
diff --git a/lisp/org/org-fold-core.el b/lisp/org/org-fold-core.el
index 4f737af4ef..0855e6f39c 100644
--- a/lisp/org/org-fold-core.el
+++ b/lisp/org/org-fold-core.el
@@ -1,6 +1,6 @@
;;; org-fold-core.el --- Folding buffer text -*- lexical-binding: t; -*-
;;
-;; Copyright (C) 2020, 2023-2020 Free Software Foundation, Inc.
+;; Copyright (C) 2020-2023 Free Software Foundation, Inc.
;;
;; Author: Ihor Radchenko <yantar92 at gmail dot com>
;; Keywords: folding, invisible text
diff --git a/lisp/org/org-fold.el b/lisp/org/org-fold.el
index 93429bb302..1b7ca22b04 100644
--- a/lisp/org/org-fold.el
+++ b/lisp/org/org-fold.el
@@ -1,6 +1,6 @@
;;; org-fold.el --- Folding of Org entries -*- lexical-binding: t; -*-
;;
-;; Copyright (C) 2020, 2023-2020 Free Software Foundation, Inc.
+;; Copyright (C) 2020-2023 Free Software Foundation, Inc.
;;
;; Author: Ihor Radchenko <yantar92 at gmail dot com>
;; Keywords: folding, invisible text
diff --git a/lisp/org/org-macs.el b/lisp/org/org-macs.el
index b09be05b7c..72929cdd26 100644
--- a/lisp/org/org-macs.el
+++ b/lisp/org/org-macs.el
@@ -74,11 +74,15 @@ Version mismatch is commonly encountered in the following
situations:
loading of the newer Org version.
It is recommended to put
- (straight-use-package 'org)
+
+ %s
+
early in the config. Ideally, right after the straight.el
bootstrap. Moving `use-package' :straight declaration may not be
sufficient if the corresponding `use-package' statement is
- deferring the loading.")
+ deferring the loading."
+ ;; Avoid `warn' replacing "'" with "’" (see `format-message').
+ "(straight-use-package 'org)")
(error "Org version mismatch. Make sure that correct `load-path' is set
early in init.el")))
;; We rely on org-macs when generating Org version. Checking Org
diff --git a/lisp/org/org-persist.el b/lisp/org/org-persist.el
index 8acf678d42..c3650c167e 100644
--- a/lisp/org/org-persist.el
+++ b/lisp/org/org-persist.el
@@ -161,7 +161,7 @@
(declare-function org-at-heading-p "org" (&optional invisible-not-ok))
-(defconst org-persist--storage-version "2.7"
+(defconst org-persist--storage-version "3.1"
"Persistent storage layout version.")
(defgroup org-persist nil
@@ -431,25 +431,27 @@ Return PLIST."
(when key (remhash (cons cont (list :key key))
org-persist--index-hash))))
(setq org-persist--index (delq existing org-persist--index)))))
-(defun org-persist--get-collection (container &optional associated &rest misc)
+(defun org-persist--get-collection (container &optional associated misc)
"Return or create collection used to store CONTAINER for ASSOCIATED.
When ASSOCIATED is nil, it is a global CONTAINER.
ASSOCIATED can also be a (:buffer buffer) or buffer, (:file file-path)
or file-path, (:inode inode), (:hash hash), or or (:key key).
-MISC, if non-nil will be appended to the collection."
+MISC, if non-nil will be appended to the collection. It must be a plist."
(unless (and (listp container) (listp (car container)))
(setq container (list container)))
(setq associated (org-persist--normalize-associated associated))
- (unless (equal misc '(nil))
- (setq associated (append associated misc)))
+ (when (and misc (or (not (listp misc)) (= 1 (% (length misc) 2))))
+ (error "org-persist: Not a plist: %S" misc))
(or (org-persist--find-index
`( :container ,(org-persist--normalize-container container)
:associated ,associated))
(org-persist--add-to-index
- (list :container (org-persist--normalize-container container)
- :persist-file
- (replace-regexp-in-string "^.." "\\&/" (org-id-uuid))
- :associated associated))))
+ (nconc
+ (list :container (org-persist--normalize-container container)
+ :persist-file
+ (replace-regexp-in-string "^.." "\\&/" (org-id-uuid))
+ :associated associated)
+ misc))))
;;;; Reading container data.
@@ -650,9 +652,10 @@ 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))
- (copy-file path file-copy 'overwrite)
+ (unless (file-exists-p file-copy)
+ (unless (file-exists-p (file-name-directory file-copy))
+ (make-directory (file-name-directory file-copy) t))
+ (copy-file path file-copy 'overwrite))
(format "%s-%s.%s" persist-file (md5 path) ext)))))
(defun org-persist-write:url (c collection)
@@ -719,7 +722,8 @@ last access, or a function accepting a single argument -
collection.
EXPIRY key has no effect when INHERIT is non-nil.
Optional key WRITE-IMMEDIATELY controls whether to save the container
data immediately.
-MISC will be appended to CONTAINER.
+MISC will be appended to the collection. It must be alternating :KEY
+VALUE pairs.
When WRITE-IMMEDIATELY is non-nil, the return value will be the same
with `org-persist-write'."
(unless org-persist--index (org-persist--load-index))
diff --git a/lisp/org/org-src.el b/lisp/org/org-src.el
index fd0adadb7a..9e43928111 100644
--- a/lisp/org/org-src.el
+++ b/lisp/org/org-src.el
@@ -629,83 +629,83 @@ Leave point in edit buffer."
"Fontify code block between START and END using LANG's syntax.
This function is called by Emacs' automatic fontification, as long
as `org-src-fontify-natively' is non-nil."
- (let ((lang-mode (org-src-get-lang-mode lang)))
- (when (fboundp lang-mode)
- (let ((string (buffer-substring-no-properties start end))
- (modified (buffer-modified-p))
- (org-buffer (current-buffer)))
- (remove-text-properties start end '(face nil))
- (with-current-buffer
- (get-buffer-create
- (format " *org-src-fontification:%s*" lang-mode))
- (let ((inhibit-modification-hooks nil))
- (erase-buffer)
- ;; Add string and a final space to ensure property change.
- (insert string " "))
- (unless (eq major-mode lang-mode) (funcall lang-mode))
- (font-lock-ensure)
- (let ((pos (point-min)) next)
- (while (setq next (next-property-change pos))
- ;; Handle additional properties from font-lock, so as to
- ;; preserve, e.g., composition.
- ;; FIXME: We copy 'font-lock-face property explicitly because
- ;; `font-lock-mode' is not enabled in the buffers starting from
- ;; space and the remapping between 'font-lock-face and 'face
- ;; text properties may thus not be set. See commit
- ;; 453d634bc.
- (dolist (prop (append '(font-lock-face face)
font-lock-extra-managed-props))
- (let ((new-prop (get-text-property pos prop)))
- (when new-prop
- (if (not (eq prop 'invisible))
- (put-text-property
- (+ start (1- pos)) (1- (+ start next)) prop new-prop
- org-buffer)
- ;; Special case. `invisible' text property may
- ;; clash with Org folding. Do not assign
- ;; `invisible' text property directly. Use
- ;; property alias instead.
- (let ((invisibility-spec
- (or
- ;; ATOM spec.
- (and (memq new-prop buffer-invisibility-spec)
- new-prop)
- ;; (ATOM . ELLIPSIS) spec.
- (assq new-prop buffer-invisibility-spec))))
- (with-current-buffer org-buffer
- ;; Add new property alias.
- (unless (memq 'org-src-invisible
- (cdr (assq 'invisible
char-property-alias-alist)))
- (setq-local
- char-property-alias-alist
- (cons (cons 'invisible
- (nconc (cdr (assq 'invisible
char-property-alias-alist))
- '(org-src-invisible)))
- (remove (assq 'invisible
char-property-alias-alist)
- char-property-alias-alist))))
- ;; Carry over the invisibility spec, unless
- ;; already present. Note that there might
- ;; be conflicting invisibility specs from
- ;; different major modes. We cannot do much
- ;; about this then.
- (when invisibility-spec
- (add-to-invisibility-spec invisibility-spec))
- (put-text-property
- (+ start (1- pos)) (1- (+ start next))
- 'org-src-invisible new-prop
- org-buffer)))))))
- (setq pos next)))
- (set-buffer-modified-p nil))
- ;; Add Org faces.
- (let ((src-face (nth 1 (assoc-string lang org-src-block-faces t))))
- (when (or (facep src-face) (listp src-face))
- (font-lock-append-text-property start end 'face src-face))
- (font-lock-append-text-property start end 'face 'org-block))
- ;; Clear abbreviated link folding.
- (org-fold-region start end nil 'org-link)
- (add-text-properties
- start end
- '(font-lock-fontified t fontified t font-lock-multiline t))
- (set-buffer-modified-p modified)))))
+ (let ((modified (buffer-modified-p)))
+ (remove-text-properties start end '(face nil))
+ (let ((lang-mode (org-src-get-lang-mode lang)))
+ (when (fboundp lang-mode)
+ (let ((string (buffer-substring-no-properties start end))
+ (org-buffer (current-buffer)))
+ (with-current-buffer
+ (get-buffer-create
+ (format " *org-src-fontification:%s*" lang-mode))
+ (let ((inhibit-modification-hooks nil))
+ (erase-buffer)
+ ;; Add string and a final space to ensure property change.
+ (insert string " "))
+ (unless (eq major-mode lang-mode) (funcall lang-mode))
+ (font-lock-ensure)
+ (let ((pos (point-min)) next)
+ (while (setq next (next-property-change pos))
+ ;; Handle additional properties from font-lock, so as to
+ ;; preserve, e.g., composition.
+ ;; FIXME: We copy 'font-lock-face property explicitly because
+ ;; `font-lock-mode' is not enabled in the buffers starting from
+ ;; space and the remapping between 'font-lock-face and 'face
+ ;; text properties may thus not be set. See commit
+ ;; 453d634bc.
+ (dolist (prop (append '(font-lock-face face)
font-lock-extra-managed-props))
+ (let ((new-prop (get-text-property pos prop)))
+ (when new-prop
+ (if (not (eq prop 'invisible))
+ (put-text-property
+ (+ start (1- pos)) (1- (+ start next)) prop new-prop
+ org-buffer)
+ ;; Special case. `invisible' text property may
+ ;; clash with Org folding. Do not assign
+ ;; `invisible' text property directly. Use
+ ;; property alias instead.
+ (let ((invisibility-spec
+ (or
+ ;; ATOM spec.
+ (and (memq new-prop buffer-invisibility-spec)
+ new-prop)
+ ;; (ATOM . ELLIPSIS) spec.
+ (assq new-prop buffer-invisibility-spec))))
+ (with-current-buffer org-buffer
+ ;; Add new property alias.
+ (unless (memq 'org-src-invisible
+ (cdr (assq 'invisible
char-property-alias-alist)))
+ (setq-local
+ char-property-alias-alist
+ (cons (cons 'invisible
+ (nconc (cdr (assq 'invisible
char-property-alias-alist))
+ '(org-src-invisible)))
+ (remove (assq 'invisible
char-property-alias-alist)
+ char-property-alias-alist))))
+ ;; Carry over the invisibility spec, unless
+ ;; already present. Note that there might
+ ;; be conflicting invisibility specs from
+ ;; different major modes. We cannot do much
+ ;; about this then.
+ (when invisibility-spec
+ (add-to-invisibility-spec invisibility-spec))
+ (put-text-property
+ (+ start (1- pos)) (1- (+ start next))
+ 'org-src-invisible new-prop
+ org-buffer)))))))
+ (setq pos next)))
+ (set-buffer-modified-p nil)))))
+ ;; Add Org faces.
+ (let ((src-face (nth 1 (assoc-string lang org-src-block-faces t))))
+ (when (or (facep src-face) (listp src-face))
+ (font-lock-append-text-property start end 'face src-face))
+ (font-lock-append-text-property start end 'face 'org-block))
+ ;; Clear abbreviated link folding.
+ (org-fold-region start end nil 'org-link)
+ (add-text-properties
+ start end
+ '(font-lock-fontified t fontified t font-lock-multiline t))
+ (set-buffer-modified-p modified)))
(defun org-fontify-inline-src-blocks (limit)
"Try to apply `org-fontify-inline-src-blocks-1'."
diff --git a/lisp/org/org-version.el b/lisp/org/org-version.el
index a0016265f0..dd6d92d8e5 100644
--- a/lisp/org/org-version.el
+++ b/lisp/org/org-version.el
@@ -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-61-g63e073f"))
+ (let ((org-git-version "release_9.6-81-g563a43"))
org-git-version))
(provide 'org-version)
diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
index 1e29dc747f..8d1c1103ab 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -425,7 +425,7 @@ MODE is either `c' or `cpp'."
;; Recurse.
((or "attributed_declarator" "parenthesized_declarator")
(c-ts-mode--declarator-identifier (treesit-node-child node 0 t)))
- ("pointer_declarator"
+ ((or "pointer_declarator" "reference_declarator")
(c-ts-mode--declarator-identifier (treesit-node-child node -1)))
((or "function_declarator" "array_declarator" "init_declarator")
(c-ts-mode--declarator-identifier
diff --git a/lisp/progmodes/csharp-mode.el b/lisp/progmodes/csharp-mode.el
index 42dc8f0583..eb1d578daa 100644
--- a/lisp/progmodes/csharp-mode.el
+++ b/lisp/progmodes/csharp-mode.el
@@ -696,7 +696,7 @@ compilation and evaluation time conflicts."
:feature 'expression
'((conditional_expression (identifier) @font-lock-variable-name-face)
(postfix_unary_expression (identifier)* @font-lock-variable-name-face)
- (assignment_expression (identifier) @font-lock-variable-name-face))
+ (initializer_expression (assignment_expression left: (identifier)
@font-lock-variable-name-face)))
:language 'c-sharp
:feature 'bracket
@@ -764,8 +764,12 @@ compilation and evaluation time conflicts."
(identifier) @font-lock-type-face)
(type_argument_list
(identifier) @font-lock-type-face)
- (generic_name
- (identifier) @font-lock-type-face)
+ (type_argument_list
+ (generic_name
+ (identifier) @font-lock-type-face))
+ (base_list
+ (generic_name
+ (identifier) @font-lock-type-face))
(array_type
(identifier) @font-lock-type-face)
(cast_expression (identifier) @font-lock-type-face)
@@ -773,7 +777,12 @@ compilation and evaluation time conflicts."
(type_parameter_constraints_clause
target: (identifier) @font-lock-type-face)
(type_of_expression (identifier) @font-lock-type-face)
- (object_creation_expression (identifier) @font-lock-type-face))
+ (object_creation_expression
+ type: (identifier) @font-lock-type-face)
+ (object_creation_expression
+ type: (generic_name (identifier) @font-lock-type-face))
+ (as_expression right: (identifier) @font-lock-type-face)
+ (as_expression right: (generic_name (identifier) @font-lock-type-face)))
:language 'c-sharp
:feature 'definition
@@ -793,7 +802,6 @@ compilation and evaluation time conflicts."
(record_declaration (identifier) @font-lock-type-face)
(namespace_declaration (identifier) @font-lock-type-face)
(base_list (identifier) @font-lock-type-face)
- (property_declaration (generic_name))
(property_declaration
type: (nullable_type) @font-lock-type-face
name: (identifier) @font-lock-variable-name-face)
@@ -807,29 +815,10 @@ compilation and evaluation time conflicts."
(constructor_declaration name: (_) @font-lock-type-face)
- (method_declaration type: (_) @font-lock-type-face)
+ (method_declaration type: [(identifier) (void_keyword)]
@font-lock-type-face)
+ (method_declaration type: (generic_name (identifier)
@font-lock-type-face))
(method_declaration name: (_) @font-lock-function-name-face)
- (invocation_expression
- (member_access_expression
- (generic_name (identifier) @font-lock-function-name-face)))
- (invocation_expression
- (member_access_expression
- ((identifier) @font-lock-variable-name-face
- (identifier) @font-lock-function-name-face)))
- (invocation_expression
- (identifier) @font-lock-function-name-face)
- (invocation_expression
- (member_access_expression
- expression: (identifier) @font-lock-variable-name-face))
- (invocation_expression
- function: [(generic_name (identifier)) @font-lock-function-name-face
- (generic_name (type_argument_list
- ["<"] @font-lock-bracket-face
- (identifier) @font-lock-type-face
- [">"] @font-lock-bracket-face)
- )])
-
(catch_declaration
((identifier) @font-lock-type-face))
(catch_declaration
@@ -837,13 +826,30 @@ compilation and evaluation time conflicts."
(identifier) @font-lock-variable-name-face))
(variable_declaration (identifier) @font-lock-type-face)
+ (variable_declaration (generic_name (identifier) @font-lock-type-face))
(variable_declarator (identifier) @font-lock-variable-name-face)
(parameter type: (identifier) @font-lock-type-face)
+ (parameter type: (generic_name (identifier) @font-lock-type-face))
(parameter name: (identifier) @font-lock-variable-name-face)
- (binary_expression (identifier) @font-lock-variable-name-face)
- (argument (identifier) @font-lock-variable-name-face))
+ (lambda_expression (identifier) @font-lock-variable-name-face)
+
+ (declaration_expression type: (identifier) @font-lock-type-face)
+ (declaration_expression name: (identifier) @font-lock-variable-name-face))
+
+ :language 'c-sharp
+ :feature 'function
+ '((invocation_expression
+ function: (member_access_expression
+ name: (identifier) @font-lock-function-name-face))
+ (invocation_expression
+ function: (identifier) @font-lock-function-name-face)
+ (invocation_expression
+ function: (member_access_expression
+ name: (generic_name (identifier)
@font-lock-function-name-face)))
+ (invocation_expression
+ function: (generic_name (identifier) @font-lock-function-name-face)))
:language 'c-sharp
:feature 'escape-sequence
@@ -921,7 +927,7 @@ Key bindings:
'(( comment definition)
( keyword string type)
( constant escape-sequence expression literal property)
- ( bracket delimiter error)))
+ ( function bracket delimiter error)))
;; Imenu.
(setq-local treesit-simple-imenu-settings
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 791108001d..6d192d9b33 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -205,7 +205,7 @@ chosen (interactively or automatically)."
(((caml-mode :language-id "ocaml")
(tuareg-mode :language-id "ocaml")
reason-mode)
. ("ocamllsp"))
- (ruby-mode
+ ((ruby-mode ruby-ts-mode)
. ("solargraph" "socket" "--port" :autoport))
(haskell-mode
. ("haskell-language-server-wrapper" "--lsp"))
diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el
new file mode 100644
index 0000000000..5c9a25c1fd
--- /dev/null
+++ b/lisp/progmodes/ruby-ts-mode.el
@@ -0,0 +1,944 @@
+;;; ruby-ts-mode.el --- Major mode for editing Ruby files using tree-sitter
-*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022-2023 Free Software Foundation, Inc.
+
+;; Author: Perry Smith <pedz@easesoftware.com>
+;; Created: December 2022
+;; Keywords: ruby 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:
+
+;; This file defines ruby-ts-mode which is a major mode for editting
+;; Ruby files that uses Tree Sitter to parse the language. More
+;; information about Tree Sitter can be found in the ELisp Info pages
+;; as well as this website: https://tree-sitter.github.io/tree-sitter/
+
+;; For this major mode to work, Emacs has to be compiled with
+;; tree-sitter support, and the Ruby grammar has to be compiled and
+;; put somewhere Emacs can find it. See the docstring of
+;; `treesit-extra-load-path'.
+
+;; This mode doesn't associate itself with .rb files automatically.
+;; You can do that either by prepending to the value of
+;; `auto-mode-alist', or using `major-mode-remap-alist'.
+
+;; Tree Sitter brings a lot of power and versitility which can be
+;; broken into these features.
+
+;; * Font Lock
+
+;; The ability to color the source code is not new but what is new is
+;; the versatility to enable and disable particular font lock rules.
+;; I suggest reviewing variable treesit-font-lock-level and function
+;; treesit-font-lock-recompute-features to get a better understanding
+;; of the following.
+
+;; Currently tree treesit-font-lock-feature-list is set with the
+;; following levels:
+;; 1: comment method-definition
+;; 2: keyword regexp string type
+;; 3: builtin constant delimiter escape-sequence
+;; global instance
+;; interpolation literal symbol variable
+;; 4: bracket error function operator punctuation
+
+;; Thus if treesit-font-lock-level is set to level 3 which is its
+;; default, all the features listed in levels 1 through 3 above will
+;; be enabled. i.e. those features will font lock or colorize the
+;; code accordingly. Individual features can be added and removed via
+;; treesit-font-lock-recompute-features.
+
+;; describe-face can be used to view how a face looks.
+
+;; * Indent
+
+;; ruby-ts-mode tries to adhere to the indentation related user
+;; options from ruby-mode, such as ruby-indent-level,
+;; ruby-indent-tabs-mode, and so on.
+
+;; * IMenu
+;; * Navigation
+;; * Which-func
+
+;;; Code:
+
+(require 'treesit)
+(require 'ruby-mode)
+
+(declare-function treesit-parser-create "treesit.c")
+
+(defgroup ruby-ts nil
+ "Major mode for editing Ruby code."
+ :prefix "ruby-ts-"
+ :group 'languages)
+
+(defcustom ruby-ts-highlight-predefined-constants t
+ "When non-nil, the pre-defined constants are highlighted.
+They will be highlighted the same way as the pre-defined variables."
+ :type 'boolean)
+
+(defvar ruby-ts--operators
+ '("+" "-" "*" "/" "%" "**"
+ "==" "!=" ">" "<" ">=" "<=" "<=>" "==="
+ "=" "+=" "-=" "*=" "/=" "%=" "**="
+ "&" "|" "^" "~" "<<" ">>"
+ "!" "&&" "and" "not" "or" "||"
+ "?" ":"
+ ".." "..."
+ "defined?"
+ "." "::")
+ "Ruby operators for tree-sitter font-locking.")
+
+(defvar ruby-ts--delimiters '("," ";")
+ "Ruby's punctuation characters.")
+
+(defvar ruby-ts--predefined-constants
+ (rx (or "ARGF" "ARGV" "DATA" "ENV" "RUBY_COPYRIGHT"
+ "RUBY_DESCRIPTION" "RUBY_ENGINE" "RUBY_ENGINE_VERSION"
+ "RUBY_PATCHLEVEL" "RUBY_PLATFORM" "RUBY_RELEASE_DATE"
+ "RUBY_REVISION" "RUBY_VERSION" "STDERR" "STDIN" "STDOUT"
+ "TOPLEVEL_BINDING"))
+ "Ruby predefined global constants.
+These are currently unused")
+
+(defvar ruby-ts--predefined-variables
+ (rx (or "$!" "$@" "$~" "$&" "$‘" "$‘" "$+" "$=" "$/" "$\\" "$," "$;"
+ "$." "$<" "$>" "$_" "$*" "$$" "$?" "$:" "$LOAD_PATH"
+ "$LOADED_FEATURES" "$DEBUG" "$FILENAME" "$stderr" "$stdin"
+ "$stdout" "$VERBOSE" "$-a" "$-i" "$-l" "$-p"
+ (seq "$" (+ digit))))
+ "Ruby global variables (but not global constants.")
+
+(defconst ruby-ts--class-or-module-regex
+ (rx string-start
+ (or "class" "module" "singleton_class")
+ string-end)
+ "Regular expression that matches a class or module's node type.")
+
+(defconst ruby-ts--method-regex
+ (rx string-start
+ (or "method" "singleton_method")
+ string-end)
+ "Regular expression matching methods and singleton methods.")
+
+(defconst ruby-ts--statement-container-regexp
+ (rx string-start
+ (or "program"
+ "block_body"
+ "begin_block"
+ "end_block"
+ "do"
+ "else"
+ "then"
+ "ensure"
+ "body_statement"
+ "parenthesized_statements"
+ "interpolation")
+ string-end)
+ "Regular expression of the nodes that can constain statements.")
+
+(defun ruby-ts--lineno (node)
+ "Return line number of NODE's start."
+ (line-number-at-pos (treesit-node-start node)))
+
+;; doc/keywords.rdoc in the Ruby git repository considers these to be
+;; reserved keywords. If these keywords are added to the list, it
+;; causes the font-lock to stop working.
+;;
+;; "__ENCODING__" "__FILE__" "__LINE__" "false" "self" "super" "true"
+;;
+;; "nil" (which does not exhibit this issue) is also considered a
+;; keyword but I removed it and added it as a constant.
+;;
+(defvar ruby-ts--keywords
+ '("BEGIN" "END" "alias" "and" "begin" "break" "case" "class"
+ "def" "defined?" "do" "else" "elsif" "end" "ensure" "for"
+ "if" "in" "module" "next" "not" "or" "redo" "rescue"
+ "retry" "return" "then" "undef" "unless" "until" "when"
+ "while" "yield")
+ "Ruby keywords for tree-sitter font-locking.")
+
+(defun ruby-ts--comment-font-lock (node override start end &rest _)
+ "Apply font lock to comment NODE within START and END.
+Applies `font-lock-comment-delimiter-face' and
+`font-lock-comment-face' See `treesit-fontify-with-override' for
+values of OVERRIDE"
+ ;; Emperically it appears as if (treesit-node-start node) will be
+ ;; where the # character is at and (treesit-node-end node) will be
+ ;; the end of the line
+ (let* ((node-start (treesit-node-start node))
+ (plus-1 (1+ node-start))
+ (node-end (treesit-node-end node))
+ (text (treesit-node-text node t)))
+ (if (and (>= node-start start)
+ (<= plus-1 end)
+ (string-match-p "\\`#" text))
+ (treesit-fontify-with-override node-start plus-1
+ font-lock-comment-delimiter-face
override))
+ (treesit-fontify-with-override (max plus-1 start) (min node-end end)
+ font-lock-comment-face override)))
+
+(defun ruby-ts--font-lock-settings (language)
+ "Tree-sitter font-lock settings for Ruby."
+ (treesit-font-lock-rules
+ :language language
+ :feature 'comment
+ '((comment) @ruby-ts--comment-font-lock)
+
+ :language language
+ :feature 'builtin
+ `(((global_variable) @var (:match ,ruby-ts--predefined-variables @var))
@font-lock-builtin-face
+ ,@(when ruby-ts-highlight-predefined-constants
+ `(((constant) @var (:match ,ruby-ts--predefined-constants @var))
@font-lock-builtin-face)))
+
+ :language language
+ :feature 'keyword
+ `([,@ruby-ts--keywords] @font-lock-keyword-face)
+
+ :language language
+ :feature 'constant
+ '((true) @font-lock-doc-markup-face
+ (false) @font-lock-doc-markup-face
+ (nil) @font-lock-doc-markup-face
+ (self) @font-lock-doc-markup-face
+ (super) @font-lock-doc-markup-face)
+
+ :language language
+ :feature 'symbol
+ '((bare_symbol) @font-lock-constant-face
+ (delimited_symbol (string_content) @font-lock-constant-face)
+ (hash_key_symbol) @font-lock-constant-face
+ (simple_symbol) @font-lock-constant-face)
+
+ ;; Before 'operator so (unary) works.
+ :language language
+ :feature 'literal
+ '((unary ["+" "-"] [(integer) (rational) (float) (complex)])
@font-lock-number-face
+ (integer) @font-lock-number-face
+ (float) @font-lock-number-face
+ (complex) @font-lock-number-face
+ (rational) @font-lock-number-face)
+
+ ;; Also before 'operator because % and / are operators
+ :language language
+ :feature 'regexp
+ '((regex "/" @font-lock-regexp-grouping-construct)
+ (regex _ (string_content) @font-lock-regexp-grouping-backslash))
+
+ :language language
+ :feature 'operator
+ `("!" @font-lock-negation-char-face
+ [,@ruby-ts--operators] @font-lock-operator-face)
+
+ ;; TODO: Consider using a different face for string delimiters.
+ ;; font-lock-delimiter-face is not a good choice, though, because it
+ ;; looks like 'default' in the default theme, and its documented purpose
+ ;; is characters like commas, semicolons, etc.
+ :language language
+ :feature 'string
+ '((delimited_symbol [ ":\"" "\"" ] @font-lock-string-face)
+ (string "\"" @font-lock-string-face)
+ (string_array [ "%w(" ")" ] @font-lock-delimiter-face)
+ (subshell "`" @font-lock-delimiter-face)
+ (symbol_array [ "%i(" ")"] @font-lock-delimiter-face))
+
+ :language language
+ :feature 'string
+ '((string_content) @font-lock-string-face
+ (heredoc_beginning) @font-lock-string-face
+ (heredoc_content) @font-lock-string-face
+ (heredoc_end) @font-lock-string-face)
+
+ :language language
+ :feature 'interpolation
+ '((interpolation "#{" @font-lock-doc-face)
+ (interpolation "}" @font-lock-doc-face))
+
+ :language language
+ :feature 'type
+ '((constant) @font-lock-type-face)
+
+ :language language
+ :feature 'global
+ '((global_variable) @font-lock-variable-name-face)
+
+ :language language
+ :feature 'instance
+ '((instance_variable) @font-lock-variable-name-face)
+
+ :language language
+ :feature 'method-definition
+ '((method
+ name: (identifier) @font-lock-function-name-face)
+ (singleton_method
+ name: (identifier) @font-lock-function-name-face)
+ (method
+ name: (setter) @font-lock-function-name-face))
+
+ ;; Yuan recommends also putting method definitions into the
+ ;; 'function' category (thus keeping it in both). I've opted to
+ ;; just use separate categories for them -- dgutov.
+ :language language
+ :feature 'function
+ '((call
+ method: (identifier) @font-lock-function-name-face))
+
+ :language language
+ :feature 'error
+ '((ERROR) @font-lock-warning-face)
+
+ :feature 'escape-sequence
+ :language language
+ :override t
+ '((escape_sequence) @font-lock-escape-face)
+
+ :language language
+ :feature 'bracket
+ '((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face)
+
+ :language language
+ :feature 'punctuation
+ `(([,@ruby-ts--delimiters] @font-lock-delimiter-face))))
+
+(defun ruby-ts--first-non-comment-child (node)
+ "Return the first named child of NODE that is not a comment."
+ (let ((child (treesit-node-child node 0 t)))
+ (while (and child
+ (equal "comment" (treesit-node-type child)))
+ (setq child (treesit-node-next-sibling child t)))
+ child))
+
+;;
+;; These routines would be better added to treesit.el They are
+;; intended to be used with indent rules
+;;
+;; I think this is over simplified but basically
+;; treesit--simple-indent-eval calls the result with node, parent, and
+;; bol. Thus all of these functions return a lambda that accepts three
+;; arguments. Somewhere something explains that &rest should always
+;; be used in case extra arguments are added in the future.
+;;
+
+(defun ruby-ts--type-pred (regexp)
+ "Return predicate taking a node returning non-nil if REGEXP matches type of
node."
+ (lambda (node)
+ (string-match-p regexp (treesit-node-type node))))
+
+(defun ruby-ts--parent-node (_n parent &rest _)
+ "Return the PARENT node matching ident rule."
+ parent)
+
+(defun ruby-ts--align-keywords (pred)
+ "Return either start or bol of PRED.
+PRED should specify a node that is listed in
+`ruby-alignable-keywords'. If PRED is listed in user option
+`ruby-align-to-stmt-keywords', then return the BOL of PRED.
+Otherwise return start of PRED."
+ (lambda (node parent bol &rest rest)
+ (let* ((pred-node (funcall pred node parent bol rest))
+ (temp (treesit-node-start pred-node))
+ (keyword (treesit-node-type pred-node))
+ (bol (ruby-smie--indent-to-stmt-p keyword)))
+ (when temp
+ (if bol
+ (save-excursion
+ (goto-char temp)
+ (back-to-indentation)
+ (point))
+ temp)))))
+
+(defun ruby-ts--bol (pred)
+ "Return bol of PRED.
+PRED should take (node parent bol &rest rest) and return a node.
+Returns bol of the current line if PRED returns nil."
+ (lambda (node parent bol &rest rest)
+ (save-excursion
+ (let ((temp (treesit-node-start (funcall pred node parent bol rest))))
+ (if temp
+ (goto-char temp))
+ (back-to-indentation)
+ (point)))))
+
+(defun ruby-ts--grand-parent-node (_n parent &rest _)
+ "Return parent of PARENT node."
+ (treesit-node-parent parent))
+
+(defun ruby-ts--align-chain-p (&rest _)
+ "Return value of `ruby-align-chained-calls'."
+ ruby-align-chained-calls)
+
+(defun ruby-ts--parenless-call-arguments-indent-p (&rest _)
+ "Return value of `ruby-parenless-call-arguments-indent'."
+ ruby-parenless-call-arguments-indent)
+
+(defun ruby-ts--align-chain (_n parent &rest _)
+ "Align chained method call.
+Align NODE which will be the dot (.) to the dot of the
+first (outermost) call in the chain. See
+`ruby-align-chained-calls' for details. PARENT will be the
+\"call\" node. Called only when `ruby-align-chained-calls' is
+non-nil."
+ (let* (first-call )
+ (while (and parent
+ (setq first-call (treesit-node-parent parent))
+ (string-match-p "call" (treesit-node-type first-call)))
+ (setq parent first-call))
+ (treesit-node-start (treesit-search-subtree parent "\\." nil t))))
+
+(defun ruby-ts--same-line-args-p (_n parent &rest _)
+ "Return non-nil when first argument is on the same line as the method.
+PARENT will be argument_list. NODE can be the close paren."
+ (let* ((method (treesit-node-parent parent))
+ (first-param (ruby-ts--first-non-comment-child parent)))
+ (= (ruby-ts--lineno method) (ruby-ts--lineno first-param))))
+
+(defun ruby-ts--same-line-params-p (_n parent &rest _)
+ "Return non-nil when first parameter is on the same line as the method.
+PARENT will be method_parameters. NODE can be the close paren."
+ (let* ((method (treesit-node-parent parent))
+ (first-param (ruby-ts--first-non-comment-child parent)))
+ (= (ruby-ts--lineno method) (ruby-ts--lineno first-param))))
+
+(defun ruby-ts--param-indent (_n parent &rest _)
+ "Indent parameters that start on next line.
+Given: NODE is the parameter. PARENT is
+method_parameters. `ruby-ts--same-line-params-p' is nil.
+Indent according to `ruby-method-params-indent'.
+
+If `ruby-method-params-indent' is 0
+def foo(
+ param1,
+ param2
+)
+
+Params start on next line, `ruby-method-params-indent' is t
+def foo(
+ param1,
+ param2
+ )"
+ (let ((method (treesit-node-parent parent)))
+ (if (eq t ruby-method-params-indent)
+ ;; For methods, the "name" is the name of the method but for
+ ;; singleton methods, we need to find "object"
+ (let* ((singleton (equal "singleton_method" (treesit-node-type
method)))
+ (name-node (treesit-node-child-by-field-name
+ method
+ (if singleton "object" "name"))))
+ ;; (message "name-node: %S" name-node)
+ (treesit-node-start name-node))
+ ;; Small Danger: if the method name plus the parent is less than
+ ;; `ruby-method-params-indent', then the addition will put the
+ ;; result on the next line and indented incorrectly. There are
+ ;; plausible ways to fix this but the probability seems rather
+ ;; remote.
+ (+ (treesit-node-start method) (or ruby-method-params-indent 0)))))
+
+(defun ruby-ts--true (&rest _)
+ "I have no idea why I can't just put t but I can put 0."
+ t)
+
+(defun ruby-ts--same-line-hash-array-p (_n parent &rest _)
+ "Return non-nil if first element and open brace are on the same line.
+NODE is the element or closing brace or bracket. PARENT is the
+array or hash."
+ (let* ((open-brace (treesit-node-child parent 0 nil))
+ (first-child (ruby-ts--first-non-comment-child parent)))
+ (= (ruby-ts--lineno open-brace) (ruby-ts--lineno first-child))))
+
+(defun ruby-ts--assignment-ancestor (node &rest _)
+ "Return the assignment ancestor of NODE if any."
+ (treesit-parent-until node (ruby-ts--type-pred "\\`assignment\\'")))
+
+(defun ruby-ts--statement-ancestor (node &rest _)
+ "Return the statement ancestor of NODE if any.
+A statement is defined as a child of a statement container where
+a statement container is a node that matches
+`ruby-ts--statement-container-regexp'."
+ (let* ((statement node)
+ (parent (treesit-node-parent statement)))
+ (while (and parent
+ statement
+ (not (string-match-p ruby-ts--statement-container-regexp
+ (treesit-node-type parent))))
+ (setq statement parent
+ parent (treesit-node-parent parent)))
+ statement))
+
+(defun ruby-ts--is-in-condition (node &rest _)
+ "Return the condition node if NODE is within a condition."
+ (while (and node
+ (not (equal "condition" (treesit-node-field-name node)))
+ (not (string-match-p ruby-ts--statement-container-regexp
+ (treesit-node-type node))))
+ (setq node (treesit-node-parent node)))
+ (and (equal "condition" (treesit-node-field-name node)) node))
+
+(defun ruby-ts--endless-method (node &rest _)
+ "Return the expression node if NODE is in an endless method.
+i.e. expr of def foo(args) = expr is returned."
+ (let* ((method node))
+ (while (and method
+ (not (string-match-p ruby-ts--method-regex (treesit-node-type
method))))
+ (setq method (treesit-node-parent method)))
+ (when method
+ (if (equal "=" (treesit-node-type (treesit-node-child method 3 nil)))
+ (treesit-node-child method 4 nil)))))
+
+;;
+;; end of functions that can be used for queries
+;;
+
+(defun ruby-ts--indent-rules ()
+ "Indent rules supported by `ruby-ts-mode'."
+ (let ((common
+ `(
+ ;; Slam all top level nodes to the left margin
+ ((parent-is "program") parent 0)
+
+ ;; Do not indent here docs or the end. Not sure why it
+ ;; takes the grand-parent but ok fine.
+ ((n-p-gp nil nil "heredoc_body") no-indent 0)
+ ((parent-is "heredoc_body") no-indent 0)
+ ((node-is "heredoc_body") no-indent 0)
+ ;; Do not indent multiline regexp
+ ((n-p-gp nil nil "regex") no-indent 0)
+ ((parent-is "regex") no-indent 0)
+
+ ;; if then else elseif notes:
+ ;;
+ ;; 1. The "then" starts at the end of the line that ends
+ ;; the if condition which can be on a different line
+ ;; from the "if".
+ ;;
+ ;; 2. If there is an "elsif", it is a sibling to the then
+ ;; BUT the "else" that follows is now a child of the
+ ;; "elsif".
+ ;;
+ ;; 3. The statements within each of these are direct
+ ;; children. There is no intermediate construct such
+ ;; as a block_statement.
+ ;;
+ ;; I'm using very restrictive patterns hoping to reduce rules
+ ;; triggering unintentionally.
+ ((match "else" "if")
+ (ruby-ts--align-keywords ruby-ts--parent-node) 0)
+ ((match "elsif" "if")
+ (ruby-ts--align-keywords ruby-ts--parent-node) 0)
+ ((match "end" "if")
+ (ruby-ts--align-keywords ruby-ts--parent-node) 0)
+ ((n-p-gp nil "then\\|else\\|elsif" "if\\|unless")
+ (ruby-ts--align-keywords ruby-ts--grand-parent-node)
ruby-indent-level)
+
+ ;; case expression: when, in_clause, and else are all
+ ;; children of case. when and in_clause have pattern and
+ ;; body as fields. body has "then" and then the statemets.
+ ;; i.e. the statements are not children of when but then.
+ ;; But for the statements are children of else.
+ ((match "when" "case")
+ (ruby-ts--align-keywords ruby-ts--parent-node) 0)
+ ((match "in_clause" "case")
+ (ruby-ts--align-keywords ruby-ts--parent-node) 0)
+ ((match "else" "case")
+ (ruby-ts--align-keywords ruby-ts--parent-node) 0)
+ ((match "end" "case")
+ (ruby-ts--align-keywords ruby-ts--parent-node) 0)
+ ((n-p-gp nil "then" "when") grand-parent ruby-indent-level)
+ ((n-p-gp nil "then" "in_clause") grand-parent ruby-indent-level)
+ ((n-p-gp nil "else" "case") parent ruby-indent-level)
+
+ ;; The beauty of inconsistency :-)
+ ;; while / until have only "do" as a child. The "end" is a
+ ;; child of "do".
+ ((n-p-gp "end" "do" "while\\|until")
+ (ruby-ts--align-keywords ruby-ts--grand-parent-node) 0)
+ ((n-p-gp nil "do" "while\\|until")
+ (ruby-ts--align-keywords ruby-ts--grand-parent-node)
ruby-indent-level)
+ ;; begin can have rescue, ensure, else, and end.
+ ;; statements are a child of begin. rescue, ensure, else,
+ ;; and end are also children of begin. rescue has a then
+ ;; as a child thus statements will be grand children of
+ ;; rescue.
+ ((n-p-gp nil "then" "rescue")
+ (ruby-ts--align-keywords ruby-ts--grand-parent-node)
ruby-indent-level)
+ ((n-p-gp nil "ensure\\|else" "begin")
+ (ruby-ts--align-keywords ruby-ts--parent-node) ruby-indent-level)
+ ((match "rescue\\|ensure\\|else\\|end" "begin")
+ (ruby-ts--align-keywords ruby-ts--parent-node) 0)
+ ((parent-is "begin") ;last
+ (ruby-ts--align-keywords ruby-ts--parent-node) ruby-indent-level)
+
+ ;; for ... I don't think I have ever used a for loop in
+ ;; Ruby. The "in" (not an in_clause) and "do" are
+ ;; children. The statements are children of the "do".
+ ;; And, of course, the "end" is a child of the "do".
+ ((n-p-gp "end" "do" "for")
+ (ruby-ts--align-keywords ruby-ts--grand-parent-node) 0)
+ ((n-p-gp nil "do" "for")
+ (ruby-ts--align-keywords ruby-ts--grand-parent-node)
ruby-indent-level)
+
+ ;; method has a "body_statement" and the "end" as children.
+ ;; The body_statement can have rescue, ensure, and else as
+ ;; well as statements. Note that the first statement of a
+ ;; body_statement hits the node as "body_statement" and not
+ ;; as the assignment, etc.
+ ((match "end" ,ruby-ts--method-regex)
+ (ruby-ts--align-keywords ruby-ts--parent-node) 0)
+ ((n-p-gp "\\`\\(rescue\\|ensure\\|else\\)\\'" "body_statement"
,ruby-ts--method-regex)
+ (ruby-ts--align-keywords ruby-ts--grand-parent-node) 0)
+ ((n-p-gp nil "rescue\\|ensure\\|else" "body_statement") parent
ruby-indent-level)
+ ((match "body_statement" ,ruby-ts--method-regex) ;first statement
+ (ruby-ts--align-keywords ruby-ts--parent-node) ruby-indent-level)
+ ((n-p-gp nil "body_statement" ,ruby-ts--method-regex) ;other
statements
+ (ruby-ts--align-keywords ruby-ts--grand-parent-node)
ruby-indent-level)
+
+ ;; Chained calls:
+ ;; if `ruby-align-chained-calls' is true, the first query
+ ;; matches and the node is aligned under the first dot (.);
+ ;; else the second query aligns
+ ;; `ruby-indent-level' spaces in from the parent.
+ ((and ruby-ts--align-chain-p (match "\\." "call"))
ruby-ts--align-chain 0)
+ ((match "\\." "call") parent ruby-indent-level)
+
+ ;; ruby-indent-after-block-in-continued-expression
+ ((match "begin" "assignment") parent ruby-indent-level)
+
+ ;; method parameters -- four styles:
+ ;; 1) With paren, first arg on same line:
+ ((and (query "(method_parameters \"(\" _ @indent)")
+ ruby-ts--same-line-params-p
+ (node-is ")"))
+ first-sibling 0)
+ ((and (query "(method_parameters \"(\" _ @indent)")
+ ruby-ts--same-line-params-p)
+ first-sibling 1)
+ ;; ;; 2) With paren, first arg on next line,
ruby-method-params-indent eq t
+ ;; ;; 3) With paren, first arg on next line,
ruby-method-params-indent neq t
+ ((and (query "(method_parameters \"(\" _ @indent)") (node-is ")"))
ruby-ts--param-indent 0)
+ ((query "(method_parameters \"(\" _ @indent)")
ruby-ts--param-indent ruby-indent-level)
+ ;; 4) No paren:
+ ((parent-is "method_parameters") first-sibling 0)
+
+ ;; Argument lists:
+ ;; 1) With paren, 1st arg on same line
+ ((and (query "(argument_list \"(\" _ @indent)")
+ ruby-ts--same-line-args-p
+ (node-is ")"))
+ first-sibling 0)
+ ((and (query "(argument_list \"(\" _ @indent)")
+ ruby-ts--same-line-args-p)
+ first-sibling 1)
+ ;; 2) With paren, 1st arg on next line
+ ((and (query "(argument_list \"(\" _ @indent)")
+ (node-is ")"))
+ (ruby-ts--bol ruby-ts--grand-parent-node) 0)
+ ((query "(argument_list \"(\" _ @indent)")
+ (ruby-ts--bol ruby-ts--grand-parent-node) ruby-indent-level)
+ ;; 3) No paren, ruby-parenless-call-arguments-indent is t
+ ((and ruby-ts--parenless-call-arguments-indent-p (parent-is
"argument_list"))
+ first-sibling 0)
+ ;; 4) No paren, ruby-parenless-call-arguments-indent is nil
+ ((parent-is "argument_list") (ruby-ts--bol
ruby-ts--grand-parent-node) ruby-indent-level)
+
+ ;; Old... probably too simple
+ ((parent-is "block_parameters") first-sibling 1)
+
+ ((and (parent-is "binary")
+ (or ruby-ts--assignment-ancestor
+ ruby-ts--is-in-condition
+ ruby-ts--endless-method))
+ first-sibling 0)
+
+ ;; ruby-mode does not touch these...
+ ((match "bare_string" "string_array") no-indent 0)
+
+ ;; hash and array other than assignments. Note that the
+ ;; first sibling is the "{" or "[". There is a special
+ ;; case where the hash is an argument to a method. These
+ ;; need to be processed first.
+
+ ((and ruby-ts--same-line-hash-array-p (match "}" "hash"))
+ first-sibling 0)
+ ((and ruby-ts--same-line-hash-array-p (parent-is "hash"))
+ (nth-sibling 0 ruby-ts--true) 0)
+ ((and ruby-ts--same-line-hash-array-p (match "]" "array"))
+ first-sibling 0)
+ ((and ruby-ts--same-line-hash-array-p (parent-is "array"))
+ (nth-sibling 0 ruby-ts--true) 0)
+
+ ;; NOTE to folks trying to understand my insanity...
+ ;; I having trouble understanding the "logic" of why things
+ ;; are indented like they are so I am adding special cases
+ ;; hoping at some point I will be struck by lightning.
+ ((and (n-p-gp "}" "hash" "pair")
+ (not ruby-ts--same-line-hash-array-p))
+ grand-parent 0)
+ ((and (n-p-gp "pair" "hash" "pair")
+ (not ruby-ts--same-line-hash-array-p))
+ grand-parent ruby-indent-level)
+ ((and (n-p-gp "}" "hash" "method")
+ (not ruby-ts--same-line-hash-array-p))
+ grand-parent 0)
+ ((and (n-p-gp "pair" "hash" "method")
+ (not ruby-ts--same-line-hash-array-p))
+ grand-parent ruby-indent-level)
+
+ ((n-p-gp "}" "hash" "assignment") (ruby-ts--bol
ruby-ts--grand-parent-node) 0)
+ ((n-p-gp nil "hash" "assignment") (ruby-ts--bol
ruby-ts--grand-parent-node) ruby-indent-level)
+ ((n-p-gp "]" "array" "assignment") (ruby-ts--bol
ruby-ts--grand-parent-node) 0)
+ ((n-p-gp nil "array" "assignment") (ruby-ts--bol
ruby-ts--grand-parent-node) ruby-indent-level)
+
+ ((n-p-gp "}" "hash" "argument_list") first-sibling 0)
+ ((n-p-gp nil "hash" "argument_list") first-sibling
ruby-indent-level)
+ ((n-p-gp "]" "array" "argument_list") first-sibling 0)
+ ((n-p-gp nil "array" "argument_list") first-sibling
ruby-indent-level)
+
+ ((match "}" "hash") first-sibling 0)
+ ((parent-is "hash") first-sibling ruby-indent-level)
+ ((match "]" "array") first-sibling 0)
+ ((parent-is "array") first-sibling ruby-indent-level)
+
+ ;; If the previous method isn't finished yet, this will get
+ ;; the next method indented properly.
+ ((n-p-gp ,ruby-ts--method-regex "body_statement"
,ruby-ts--class-or-module-regex)
+ (ruby-ts--bol ruby-ts--grand-parent-node) ruby-indent-level)
+
+ ;; Match the end of a class / modlue
+ ((match "end" ,ruby-ts--class-or-module-regex) parent 0)
+
+ ;; A "do_block" has a "body_statement" child which has the
+ ;; statements as children within it. The problem is that
+ ;; the first statement starts at the same point as the
+ ;; body_statement and so treesit-simple-indent is called
+ ;; with node set to body_statement on the first statement
+ ;; but with node set to the statement and parent set to
+ ;; body_statement for all others. ... Fine. Be that way.
+ ;; Ditto for "block" and "block_body"
+ ((node-is "body_statement") parent-bol ruby-indent-level)
+ ((parent-is "body_statement") (ruby-ts--bol
ruby-ts--grand-parent-node) ruby-indent-level)
+ ((match "end" "do_block") parent-bol 0)
+ ((n-p-gp "block_body" "block" nil) parent-bol ruby-indent-level)
+ ((n-p-gp nil "block_body" "block") (ruby-ts--bol
ruby-ts--grand-parent-node) ruby-indent-level)
+ ((match "}" "block") (ruby-ts--bol ruby-ts--grand-parent-node) 0)
+
+ ;; Chained strings
+ ((match "string" "chained_string") first-sibling 0)
+
+ ;; Try and indent two spaces when all else fails.
+ (catch-all parent-bol ruby-indent-level))))
+ `((ruby . ,common))))
+
+(defun ruby-ts--class-or-module-p (node)
+ "Predicate if NODE is a class or module."
+ (string-match-p ruby-ts--class-or-module-regex (treesit-node-type node)))
+
+(defun ruby-ts--get-name (node)
+ "Return the text of the `name' field of NODE."
+ (treesit-node-text (treesit-node-child-by-field-name node "name")))
+
+(defun ruby-ts--full-name (node)
+ "Return the fully qualified name of NODE."
+ (let* ((name (ruby-ts--get-name node))
+ (delimiter "#"))
+ (while (setq node (treesit-parent-until node #'ruby-ts--class-or-module-p))
+ (setq name (concat (ruby-ts--get-name node) delimiter name))
+ (setq delimiter "::"))
+ name))
+
+(defun ruby-ts--imenu-helper (node)
+ "Convert a treesit sparse tree NODE in an imenu list.
+Helper for `ruby-ts--imenu' which converts a treesit sparse
+NODE into a list of imenu ( name . pos ) nodes"
+ (let* ((ts-node (car node))
+ (subtrees (mapcan #'ruby-ts--imenu-helper (cdr node)))
+ (name (when ts-node
+ (ruby-ts--full-name ts-node)))
+ (marker (when ts-node
+ (set-marker (make-marker)
+ (treesit-node-start ts-node)))))
+ (cond
+ ((or (null ts-node) (null name)) subtrees)
+ ;; Don't include the anonymous "class" and "module" nodes
+ ((string-match-p "(\"\\(class\\|module\\)\")"
+ (treesit-node-string ts-node))
+ nil)
+ (subtrees
+ `((,name ,(cons name marker) ,@subtrees)))
+ (t
+ `((,name . ,marker))))))
+
+;; For now, this is going to work like ruby-mode and return a list of
+;; class, modules, def (methods), and alias. It is likely that this
+;; can be rigged to be easily extended.
+(defun ruby-ts--imenu ()
+ "Return Imenu alist for the current buffer."
+ (let* ((root (treesit-buffer-root-node))
+ (nodes (treesit-induce-sparse-tree root
"^\\(method\\|alias\\|class\\|module\\)$")))
+ (ruby-ts--imenu-helper nodes)))
+
+(defun ruby-ts--arrow-up-start (arg)
+ "Move to the start ARG levels up or out."
+ (interactive "p")
+ (setq arg (or arg 1))
+ (let* ((pnt (point))
+ (found (treesit-node-at pnt))
+ (pos (treesit-node-start found))
+ new-pos)
+ (while (and found pos (> arg 0))
+ (setq found (treesit-node-parent found)
+ new-pos (treesit-node-start found))
+ (when (and new-pos (not (= new-pos pos)))
+ (setq arg (1- arg)
+ pos new-pos)))
+ (if pos
+ (goto-char pos)
+ (error "Something didn't work"))))
+
+(defun ruby-ts--class-name (node)
+ "Return NODE's name.
+Assumes NODE's type is \"class\" or \"method\""
+ (list
+ (treesit-node-text
+ (treesit-node-child-by-field-name
+ node
+ (if (equal "singleton_class" (treesit-node-type node)) "value" "name"))
+ t)))
+
+(defun ruby-ts--method-name (node)
+ "Return the method name of NODE.
+Assumes NODE's type is method or singleton_method."
+ (if (equal "method" (treesit-node-type node))
+ (list (treesit-node-text (treesit-node-child-by-field-name node "name")
t))
+ (let* ((children (treesit-node-children node))
+ ;; 0th is "def"
+ (first (nth 1 children))
+ (third (nth 3 children)))
+ (cond
+ ((equal "(" (treesit-node-type first))
+ (list (treesit-node-text (nth 2 children) t)
+ (treesit-node-text (nth 5 children) t)))
+ ;; ((equal "self" (treesit-node-type first))
+ ;; (list (treesit-node-text third t)))
+ (t (mapcar (lambda (n)
+ (treesit-node-text n t))
+ (list first third)))))))
+
+(defun ruby-ts-add-log-current-function ()
+ "Return the current method name as a string.
+The hash (#) is for instance methods only which are methods
+\"defined on a class\" -- which is 99% of methods. Otherwise, a
+dot (.) is used. Double colon (::) is used between classes. The
+leading double colon is not added."
+ (let* ((node (treesit-node-at (point)))
+ (method (treesit-parent-until node (ruby-ts--type-pred
ruby-ts--method-regex)))
+ (class (or method node))
+ (result nil)
+ (sep "#")
+ (method-list nil)
+ (class-list nil)
+ (method-name nil))
+
+ (when method
+ (setq method-list (ruby-ts--method-name method))
+ (unless (= 1 (length method-list))
+ (setq sep ".")))
+ (while (setq class (treesit-parent-until class
+ (ruby-ts--type-pred
+ ruby-ts--class-or-module-regex)))
+ (setq class-list (append (ruby-ts--class-name class) class-list)))
+ (setq method-name (car (last method-list))
+ method-list (butlast method-list))
+ (when (equal (car method-list) (car (last class-list)))
+ (setq method-list (cdr method-list)))
+ (dolist (ele (append class-list method-list))
+ (cond
+ ((equal "self" ele)
+ (setq sep "."))
+ ((string-match-p "\\`[^A-Z]" ele) ;not a class
+ (setq sep "."
+ result (if result
+ (concat result "::" ele)
+ ele)))
+ (t (setq result (if result
+ (concat result "::" ele)
+ ele)))))
+ (if method-name
+ (concat result sep method-name)
+ result)))
+
+(defvar-keymap ruby-ts-mode-map
+ :doc "Keymap used in Ruby mode"
+ :parent prog-mode-map
+ ;; (when ruby-use-smie
+ ;; (define-key map (kbd "M-C-d") 'smie-down-list))
+ ;; (define-key map (kbd "M-C-p") 'ruby-beginning-of-block)
+ ;; (define-key map (kbd "M-C-n") 'ruby-end-of-block)
+ "C-c {" #'ruby-toggle-block
+ "C-c '" #'ruby-toggle-string-quotes
+ "C-c C-f" #'ruby-find-library-file)
+
+;;;###autoload
+(define-derived-mode ruby-ts-mode prog-mode "Ruby"
+ "Major mode for editing Ruby, powered by tree-sitter."
+ :group 'ruby
+ :syntax-table ruby-mode-syntax-table
+
+ (setq indent-tabs-mode ruby-indent-tabs-mode)
+
+ (setq-local paragraph-start (concat "$\\|" page-delimiter))
+ (setq-local paragraph-separate paragraph-start)
+ (setq-local paragraph-ignore-fill-prefix t)
+
+ (setq-local comment-start "# ")
+ (setq-local comment-end "")
+ (setq-local comment-start-skip "#+ *")
+
+ (unless (treesit-ready-p 'ruby)
+ (error "Tree-sitter for Ruby isn't available"))
+
+ (treesit-parser-create 'ruby)
+
+ (setq-local add-log-current-defun-function
#'ruby-ts-add-log-current-function)
+
+ ;; Navigation.
+ (setq-local treesit-defun-type-regexp ruby-ts--method-regex)
+
+ ;; AFAIK, Ruby can not nest methods
+ (setq-local treesit-defun-prefer-top-level nil)
+
+ ;; Imenu.
+ (setq-local imenu-create-index-function #'ruby-ts--imenu)
+
+ (setq-local treesit-simple-indent-rules (ruby-ts--indent-rules))
+
+ ;; Font-lock.
+ (setq-local treesit-font-lock-settings (ruby-ts--font-lock-settings 'ruby))
+ ;; Level 3 is the default.
+ (setq-local treesit-font-lock-feature-list
+ '(( comment method-definition )
+ ( keyword regexp string type)
+ ( builtin constant
+ delimiter escape-sequence global
+ instance
+ interpolation literal symbol variable)
+ ( bracket error function operator punctuation)))
+
+ (treesit-major-mode-setup))
+
+(provide 'ruby-ts-mode)
+
+;;; ruby-ts-mode.el ends here
diff --git a/lisp/simple.el b/lisp/simple.el
index df045a4445..7910454e2f 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -3590,7 +3590,7 @@ Return what remains of the list."
;; said it would do.
(unless (and (= start start-mark)
(= (+ delta end) end-mark))
- (error "Changes to be undone by function different from
announced"))
+ (error "Changes undone by function are different from the
announced ones"))
(set-marker start-mark nil)
(set-marker end-mark nil))
(apply fun-args))
diff --git a/lisp/treesit.el b/lisp/treesit.el
index e6b2e4af94..bcdc9daeac 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -2810,6 +2810,8 @@ function signals an error."
(rx bos (+ anychar) ".o" eos))
"-o" ,lib-name))
;; Copy out.
+ (unless (file-exists-p out-dir)
+ (make-directory out-dir t))
(copy-file lib-name (file-name-as-directory out-dir) t t)
(message "Library installed to %s/%s" out-dir lib-name))
(when (file-exists-p workdir)
diff --git a/test/lisp/emacs-lisp/copyright-tests.el
b/test/lisp/emacs-lisp/copyright-tests.el
index ef53117461..5f8b5c6789 100644
--- a/test/lisp/emacs-lisp/copyright-tests.el
+++ b/test/lisp/emacs-lisp/copyright-tests.el
@@ -59,7 +59,8 @@
"\nCopyright 2006, 2007, 2008 Foo Bar\n\n")
(copyright-update)
(buffer-substring (- (point-max) 42) (point-max))))
- "Copyright 2006, 2007, 2008, 2022 Foo Bar\n\n")))
+ (format "Copyright 2006, 2007, 2008, %s Foo Bar\n\n"
+ (format-time-string "%Y")))))
(ert-deftest test-correct-notice ()
(should (equal
@@ -70,7 +71,8 @@
(copyright-query nil))
(copyright-update))
(buffer-string))
- "Copyright 2021 FSF\nCopyright 2021, 2022 FSF\n")))
+ (format "Copyright 2021 FSF\nCopyright 2021, %s FSF\n"
+ (format-time-string "%Y")))))
(defmacro with-copyright-fix-years-test (orig result)
`(let ((copyright-year-ranges t))
diff --git a/test/lisp/emacs-lisp/shortdoc-tests.el
b/test/lisp/emacs-lisp/shortdoc-tests.el
index 914aee633e..516d095767 100644
--- a/test/lisp/emacs-lisp/shortdoc-tests.el
+++ b/test/lisp/emacs-lisp/shortdoc-tests.el
@@ -22,6 +22,7 @@
(require 'ert)
(require 'shortdoc)
(require 'subr-x) ; `string-pad' in shortdoc group needed at run time
+(require 'regexp-opt) ; `regexp-opt-charset' not autoloaded
(defun shortdoc-tests--tree-contains (tree fun)
"Whether TREE contains a call to FUN."
diff --git a/test/lisp/progmodes/ruby-ts-mode-tests.el
b/test/lisp/progmodes/ruby-ts-mode-tests.el
new file mode 100644
index 0000000000..f48d0bf633
--- /dev/null
+++ b/test/lisp/progmodes/ruby-ts-mode-tests.el
@@ -0,0 +1,254 @@
+;;; ruby-mode-tests.el --- Test suite for ruby-mode -*- lexical-binding:t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'ert)
+(require 'ert-x)
+(require 'ruby-ts-mode)
+
+(defmacro ruby-ts-with-temp-buffer (contents &rest body)
+ (declare (indent 1) (debug t))
+ `(with-temp-buffer
+ (insert ,contents)
+ (ruby-ts-mode)
+ ,@body))
+
+(defun ruby-ts-should-indent-buffer (expected content)
+ "Assert that CONTENT turns into EXPECTED after the buffer is re-indented.
+
+The whitespace before and including \"|\" on each line is removed."
+ (ruby-ts-with-temp-buffer (ruby-ts-test-string content)
+ (indent-region (point-min) (point-max))
+ (should (string= (ruby-ts-test-string expected) (buffer-string)))))
+
+(defun ruby-ts-test-string (s &rest args)
+ (apply 'format (replace-regexp-in-string "^[ \t]*|" "" s) args))
+
+(ert-deftest ruby-ts-indent-simple ()
+ (skip-unless (treesit-available-p))
+ (ruby-ts-should-indent-buffer
+ "if foo
+ | bar
+ |end
+ |zot
+ |"
+ "if foo
+ |bar
+ | end
+ | zot
+ |"))
+
+(ert-deftest ruby-ts-align-to-stmt-keywords-t ()
+ (skip-unless (treesit-available-p))
+ (let ((ruby-align-to-stmt-keywords t))
+ (ruby-ts-should-indent-buffer
+ "foo = if bar?
+ | 1
+ |else
+ | 2
+ |end
+ |
+ |foo || begin
+ | bar
+ |end
+ |
+ |foo ||
+ | begin
+ | bar
+ | end
+ |"
+ "foo = if bar?
+ | 1
+ |else
+ | 2
+ | end
+ |
+ | foo || begin
+ | bar
+ |end
+ |
+ | foo ||
+ | begin
+ |bar
+ | end
+ |")
+ ))
+
+(ert-deftest ruby-ts-align-to-stmt-keywords-case ()
+ (skip-unless (treesit-available-p))
+ (let ((ruby-align-to-stmt-keywords '(case)))
+ (ruby-ts-should-indent-buffer
+ "b = case a
+ |when 13
+ | 6
+ |else
+ | 42
+ |end"
+ "b = case a
+ | when 13
+ | 6
+ | else
+ | 42
+ | end")))
+
+(ert-deftest ruby-ts-add-log-current-method-examples ()
+ (skip-unless (treesit-available-p))
+ (let ((pairs '(("foo" . "#foo")
+ ("C.foo" . ".foo")
+ ("self.foo" . ".foo")
+ ("<<" . "#<<"))))
+ (dolist (pair pairs)
+ (let ((name (car pair))
+ (value (cdr pair)))
+ (ruby-ts-with-temp-buffer (ruby-ts-test-string
+ "module M
+ | class C
+ | def %s
+ | _
+ | end
+ | end
+ |end"
+ name)
+ (search-backward "_")
+ (forward-line)
+ (should (string= (ruby-ts-add-log-current-function)
+ (format "M::C%s" value))))))))
+
+(ert-deftest ruby-ts-add-log-current-method-outside-of-method ()
+ (skip-unless (treesit-available-p))
+ (ruby-ts-with-temp-buffer (ruby-ts-test-string
+ "module M
+ | class C
+ | def foo
+ | end
+ | _
+ | end
+ |end")
+ (search-backward "_")
+ (should (string= (ruby-ts-add-log-current-function) "M::C"))))
+
+(ert-deftest ruby-ts-add-log-current-method-in-singleton-class ()
+ (skip-unless (treesit-available-p))
+ (ruby-ts-with-temp-buffer (ruby-ts-test-string
+ "class C
+ | class << self
+ | def foo
+ | _
+ | end
+ | end
+ |end")
+ (search-backward "_")
+ (should (string= (ruby-ts-add-log-current-function) "C.foo"))))
+
+(ert-deftest ruby-ts-add-log-current-method-namespace-shorthand ()
+ (skip-unless (treesit-available-p))
+ (ruby-ts-with-temp-buffer (ruby-ts-test-string
+ "class C::D
+ | def foo
+ | _
+ | end
+ |end")
+ (search-backward "_")
+ (should (string= (ruby-ts-add-log-current-function) "C::D#foo"))))
+
+(ert-deftest ruby-ts-add-log-current-method-after-inner-class ()
+ (skip-unless (treesit-available-p))
+ (ruby-ts-with-temp-buffer (ruby-ts-test-string
+ "module M
+ | class C
+ | class D
+ | end
+ | def foo
+ | _
+ | end
+ | end
+ |end")
+ (search-backward "_")
+ (should (string= (ruby-ts-add-log-current-function) "M::C#foo"))))
+
+(ert-deftest ruby-ts-add-log-current-method-after-inner-class-outside-methods
()
+ (skip-unless (treesit-available-p))
+ (ruby-ts-with-temp-buffer (ruby-ts-test-string
+ "module M
+ | class C
+ | class D
+ | end
+ |
+ |_
+ | end
+ |end")
+ (search-backward "_")
+ (delete-char 1)
+ (should (string= (ruby-ts-add-log-current-function) "M::C"))))
+
+(ert-deftest
ruby-ts-add-log-current-method-after-inner-class-outside-methods-with-text ()
+ (skip-unless (treesit-available-p))
+ (ruby-ts-with-temp-buffer (ruby-ts-test-string
+ "module M
+ | class C
+ | class D
+ | end
+ |
+ | FOO = 5
+ | end
+ |end")
+ (search-backward "FOO")
+ (should (string= (ruby-ts-add-log-current-function) "M::C"))))
+
+(ert-deftest ruby-ts-add-log-current-method-after-endless-method ()
+ (skip-unless (treesit-available-p))
+ (ruby-ts-with-temp-buffer (ruby-ts-test-string
+ "module M
+ | class C
+ | def foo =
+ | 4_
+ | end
+ |end")
+ (search-backward "_")
+ (delete-char 1)
+ (should (string= (ruby-ts-add-log-current-function) "M::C#foo"))))
+
+(defmacro ruby-ts-resource-file (file)
+ `(when-let ((testfile ,(or (macroexp-file-name)
+ buffer-file-name)))
+ (let ((default-directory (file-name-directory testfile)))
+ (file-truename
+ (expand-file-name (format "ruby-mode-resources/%s" ,file))))))
+
+(defmacro ruby-ts-deftest-indent (file)
+ `(ert-deftest ,(intern (format "ruby-ts-indent-test/%s" file)) ()
+ ;; :tags '(:expensive-test)
+ (skip-unless (treesit-available-p))
+ (let ((buf (find-file-noselect (ruby-ts-resource-file ,file))))
+ (unwind-protect
+ (with-current-buffer buf
+ (let ((orig (buffer-string)))
+ ;; Indent and check that we get the original text.
+ (indent-region (point-min) (point-max))
+ (should (equal (buffer-string) orig))))
+ (kill-buffer buf)))))
+
+(ruby-ts-deftest-indent "ruby-method-params-indent.rb")
+
+(provide 'ruby-ts-mode-tests)
+
+;;; ruby-ts-mode-tests.el ends here