Merge from origin/emacs-29

4520f09dd8 ; * admin/git-bisect-start: Update failing commits
2569ede9c4 Update to Org 9.6-81-g563a43
d9ed736f0a ruby-ts-mode: Remove some currently unused functions
4561844720 ruby-ts-mode: Highlight singleton method definitions and ...
0562006da3 Add ruby-ts-mode
84e7c2fbc8 Fix fontification of C++ reference return types (bug#60441)
1864b65af6 ; Minor fix for treesit--install-language-grammar-1 (bug#...
8994f87ad4 Adjust function-call fontification in csharp-ts-mode (bug...
411647a3f6 ; Fix NEWS.
7b0b17df67 Rewrite Antinews in ELisp manual for Emacs 29
f12f72b0e0 ; * lisp/simple.el (primitive-undo): Clarify error messag...
7fd822e7f5 Update Antinews in the user manual for Emacs 29
da77d70dee ; * test/lisp/emacs-lisp/copyright-tests.el: Fix and futu...
2baf9e107c Fix shortdoc-tests failure with respect to regexp-opt-cha...
5aeb8de32e ; Fix copyright years in 2 more files.

# Conflicts:
#	etc/NEWS
This commit is contained in:
Stefan Kangas 2023-01-02 12:06:37 +01:00
commit c209802f7b
27 changed files with 1639 additions and 351 deletions

View file

@ -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

View file

@ -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.
For the same reason, sub-groups in interactive regexp searches are no
longer highlighted in distinct colors.
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.
@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.
In preparation for eventual removal of Unicode support from Emacs,
we've downgraded our Unicode support to version 14.0.
@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.
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
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.
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
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
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
@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

View file

@ -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.

View file

@ -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.
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 ability of compiling symlinked @file{*.el} files, which is really
gross: copy the files instead.
@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.
@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.
The warnings about malformed @code{defcustom} types, like
double-quoting symbols in @code{choice} lists.
@end itemize
@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 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
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.
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
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.
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
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 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 variable @code{current-minibuffer-command} is no longer available,
as we found little justification for keeping it.
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 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

View file

@ -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.

View file

@ -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.
---

View file

@ -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.

View file

@ -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,

View file

@ -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)

View file

@ -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))))

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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))

View file

@ -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'."

View file

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

View file

@ -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

View file

@ -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

View file

@ -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"))

View file

@ -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

View file

@ -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))

View file

@ -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)

View file

@ -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))

View file

@ -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."

View file

@ -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