emacs-diffs
[Top][All Lists]
Advanced

[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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]