emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 2ec4e187c9: Merge from origin/emacs-29


From: Stefan Kangas
Subject: master 2ec4e187c9: Merge from origin/emacs-29
Date: Sat, 31 Dec 2022 00:45:23 -0500 (EST)

branch: master
commit 2ec4e187c9c4592f9fbd86b67854110252b80140
Merge: 57e363a451 ab1f245f1a
Author: Stefan Kangas <stefankangas@gmail.com>
Commit: Stefan Kangas <stefankangas@gmail.com>

    Merge from origin/emacs-29
    
    ab1f245f1a1 Show tree-sitter query source when signaling query error
    0b58ea0e602 ; * lisp/treesit.el (treesit--install-language-grammar-1)...
    724da28763c Add version tags and mention the new options in NEWS
    8675f4136c7 Add new options for Ruby code indentation
    4922de626f0 ; Fix doc strings of 'treesit-install-language-grammar'
    69b2aaaaded ; Fix recently modified docs of 'set-face-attribute' (bug...
    0248fc9e1ac Add treesit-install-language-grammar
    0237c5927e9 Add treesit-language-abi-version
    312f82d36f0 Change "language definition" to "language grammar" in man...
    fba35657da5 ; * lisp/progmodes/c-ts-mode.el (c-ts-mode--fill-paragrap...
    dec1b37a32b ; * doc/lispref/parsing.texi (Accessing Node Information)...
    39265abf0cc * test/lisp/net/tramp-tests.el (tramp-test32-shell-comman...
    ebf65c7e7e3 * lisp/eshell/em-tramp.el (tramp): Require also at runtim...
    
    # Conflicts:
    #       etc/NEWS
---
 doc/lispref/display.texi                           |  23 ++--
 doc/lispref/elisp.texi                             |   2 +-
 doc/lispref/modes.texi                             |   2 +-
 doc/lispref/parsing.texi                           |  72 +++++-----
 etc/NEWS.29                                        |   6 +-
 lisp/eshell/em-tramp.el                            |   5 +-
 lisp/faces.el                                      |   4 +-
 lisp/progmodes/c-ts-mode.el                        |   4 +-
 lisp/progmodes/ruby-mode.el                        | 145 ++++++++++++++++++---
 lisp/treesit.el                                    | 142 +++++++++++++++++++-
 src/treesit.c                                      |  42 ++++--
 test/lisp/net/tramp-tests.el                       |  14 +-
 .../ruby-after-operator-indent.rb                  |  29 +++++
 .../ruby-mode-resources/ruby-block-indent.rb       |  33 +++++
 .../ruby-mode-resources/ruby-method-call-indent.rb |  15 +++
 .../ruby-parenless-call-arguments-indent.rb        |  23 ++++
 test/lisp/progmodes/ruby-mode-resources/ruby.rb    |  23 ++++
 test/lisp/progmodes/ruby-mode-tests.el             |   4 +
 18 files changed, 503 insertions(+), 85 deletions(-)

diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 5397489e44..604d5bd1a2 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -3059,16 +3059,23 @@ values specified by @code{defface}.  If @var{frame} is 
@code{nil},
 this function sets the attributes for all existing frames, as well as
 for newly created frames.
 
-To @emph{unset} the value of an attribute, that is, to indicate that
-the face doesn't by itself specify a value for the attribute, the
-special value @code{unspecified} (@emph{not} @code{nil}!@:) must be
-used.
+To @emph{reset} the value of an attribute, that is, to indicate that
+the face doesn't by itself specify a value for the attribute, use the
+special value @code{unspecified} (@emph{not} @code{nil}!@:) for the
+attribute, and set the @var{frame} argument to @code{t}, in addition
+to the call with @var{frame} set to @code{nil}.  This is because the
+default attributes for newly created frames are merged with the face's
+spec in @code{defface} when a new frame is created, and so having
+@code{unspecified} in the default attributes for new frames will be
+unable to override @code{defface}; the special call to this function
+as described above will arrange for @code{defface} to be overridden.
 
 Note that the attribute-value pairs are evaluated in the order they
-are specified, except the @code{:family} and @code{:foundry}
-attributes, which are evaluated first.  This means both that only the
-last value of a given attribute will be used, and that in some cases a
-different order will give different results.  For example, when
+are specified, with the exception of the @code{:family} and
+@code{:foundry} attributes, which are evaluated first.  This means
+that if a certain attribute is specified more than once, only the last
+value will be used.  It also means that in some cases a different
+order of attributes will produce different results.  For example, when
 @code{:weight} is placed before @code{:font}, the weight value is
 applied to the current font of the face, and might be rounded to the
 closest available weight of that font, whereas when @code{:font} is
diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi
index b1bbe5e0a9..fa4bc36b25 100644
--- a/doc/lispref/elisp.texi
+++ b/doc/lispref/elisp.texi
@@ -1364,7 +1364,7 @@ Syntax Tables
 
 Parsing Program Source
 
-* Language Definitions::     Loading tree-sitter language definitions.
+* Language Grammar::         Loading tree-sitter language grammar.
 * Using Parser::             Introduction to parsers.
 * Retrieving Nodes::         Retrieving nodes from a syntax tree.
 * Accessing Node Information:: Accessing node information.
diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi
index de17969566..9eb4f120f3 100644
--- a/doc/lispref/modes.texi
+++ b/doc/lispref/modes.texi
@@ -5082,7 +5082,7 @@ shown in red color.  @c Are colors customizable? faces?
 @end defun
 
 It is also helpful to use @code{treesit-inspect-mode} (@pxref{Language
-Definitions}) when writing indentation rules.
+Grammar}) when writing indentation rules.
 
 @node Desktop Save Mode
 @section Desktop Save Mode
diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi
index 86b3bd54e7..8803991b72 100644
--- a/doc/lispref/parsing.texi
+++ b/doc/lispref/parsing.texi
@@ -30,7 +30,7 @@ available for the current Emacs session.
 
 To be able to parse the program source using the tree-sitter library
 and access the syntax tree of the program, a Lisp program needs to
-load a language definition library, and create a parser for that
+load a language grammar library, and create a parser for that
 language and the current buffer.  After that, the Lisp program can
 query the parser about specific nodes of the syntax tree.  Then, it
 can access various kinds of information about each node, and search
@@ -39,7 +39,7 @@ explains how to do all this, and also how a Lisp program can 
work with
 source files that mix multiple programming languages.
 
 @menu
-* Language Definitions::     Loading tree-sitter language definitions.
+* Language Grammar::         Loading tree-sitter language grammar.
 * Using Parser::             Introduction to parsers.
 * Retrieving Nodes::         Retrieving nodes from a syntax tree.
 * Accessing Node Information:: Accessing node information.
@@ -49,27 +49,27 @@ source files that mix multiple programming languages.
 * Tree-sitter C API::        Compare the C API and the ELisp API.
 @end menu
 
-@node Language Definitions
-@section Tree-sitter Language Definitions
-@cindex language definitions, for tree-sitter
+@node Language Grammar
+@section Tree-sitter Language Grammar
+@cindex language grammar, for tree-sitter
 
-@heading Loading a language definition
-@cindex loading language definition for tree-sitter
+@heading Loading a language grammar
+@cindex loading language grammar for tree-sitter
 
 @cindex language argument, for tree-sitter
-Tree-sitter relies on language definitions to parse text in that
-language.  In Emacs, a language definition is represented by a symbol.
-For example, the C language definition is represented as the symbol
+Tree-sitter relies on language grammar to parse text in that
+language.  In Emacs, a language grammar is represented by a symbol.
+For example, the C language grammar is represented as the symbol
 @code{c}, and @code{c} can be passed to tree-sitter functions as the
 @var{language} argument.
 
 @vindex treesit-extra-load-path
 @vindex treesit-load-language-error
 @vindex treesit-load-suffixes
-Tree-sitter language definitions are distributed as dynamic libraries.
-In order to use a language definition in Emacs, you need to make sure
+Tree-sitter language grammar are distributed as dynamic libraries.
+In order to use a language grammar in Emacs, you need to make sure
 that the dynamic library is installed on the system.  Emacs looks for
-language definitions in several places, in the following order:
+language grammar in several places, in the following order:
 
 @itemize @bullet
 @item
@@ -91,12 +91,12 @@ that signal could be one of the following:
 
 @table @code
 @item (not-found @var{error-msg} @dots{})
-This means that Emacs could not find the language definition library.
+This means that Emacs could not find the language grammar library.
 @item (symbol-error @var{error-msg})
 This means that Emacs could not find in the library the expected function
-that every language definition library should export.
+that every language grammar library should export.
 @item (version-mismatch @var{error-msg})
-This means that the version of language definition library is incompatible
+This means that the version of language grammar library is incompatible
 with that of the tree-sitter library.
 @end table
 
@@ -105,7 +105,7 @@ In all of these cases, @var{error-msg} might provide 
additional
 details about the failure.
 
 @defun treesit-language-available-p language &optional detail
-This function returns non-@code{nil} if the language definitions for
+This function returns non-@code{nil} if the language grammar for
 @var{language} exist and can be loaded.
 
 If @var{detail} is non-@code{nil}, return @code{(t . nil)} when
@@ -119,7 +119,7 @@ By convention, the file name of the dynamic library for 
@var{language} is
 @file{libtree-sitter-@var{language}.@var{ext}}, where @var{ext} is the
 system-specific extension for dynamic libraries.  Also by convention,
 the function provided by that library is named
-@code{tree_sitter_@var{language}}.  If a language definition library
+@code{tree_sitter_@var{language}}.  If a language grammar library
 doesn't follow this convention, you should add an entry
 
 @example
@@ -140,19 +140,25 @@ to the list in the variable 
@code{treesit-load-name-override-list}, where
 for a language that considers itself too ``cool'' to abide by
 conventions.
 
-@cindex language-definition version, compatibility
-@defun treesit-language-version &optional min-compatible
-This function returns the version of the language-definition
+@cindex language grammar version, compatibility
+@defun treesit-library-abi-version &optional min-compatible
+This function returns the version of the language grammar
 Application Binary Interface (@acronym{ABI}) supported by the
 tree-sitter library.  By default, it returns the latest ABI version
 supported by the library, but if @var{min-compatible} is
 non-@code{nil}, it returns the oldest ABI version which the library
-still can support.  Language definition libraries must be built for
+still can support.  language grammar libraries must be built for
 ABI versions between the oldest and the latest versions supported by
 the tree-sitter library, otherwise the library will be unable to load
 them.
 @end defun
 
+@defun treesit-language-abi-version language
+This function returns the language grammar @acronym{ABI} version of
+language grammar for @var{language} loaded by Emacs.  If
+@var{language} is unavailable, this function returns @code{nil}.
+@end defun
+
 @heading Concrete syntax tree
 @cindex syntax tree, concrete
 
@@ -210,7 +216,7 @@ punctuation characters like bracket @samp{]}, and keywords 
like
 @cindex field name, tree-sitter
 @cindex tree-sitter node field name
 @anchor{tree-sitter node field name}
-To make the syntax tree easier to analyze, many language definitions
+To make the syntax tree easier to analyze, many language grammar
 assign @dfn{field names} to child nodes.  For example, a
 @code{function_definition} node could have a @code{declarator} and a
 @code{body}:
@@ -266,13 +272,13 @@ parser in @code{(treesit-parser-list)} (@pxref{Using 
Parser}).
 @heading Reading the grammar definition
 @cindex reading grammar definition, tree-sitter
 
-Authors of language definitions define the @dfn{grammar} of a
+Authors of language grammar define the @dfn{grammar} of a
 programming language, which determines how a parser constructs a
 concrete syntax tree out of the program text.  In order to use the
 syntax tree effectively, you need to consult the @dfn{grammar file}.
 
 The grammar file is usually @file{grammar.js} in a language
-definition's project repository.  The link to a language definition's
+grammar's project repository.  The link to a language grammar's
 home page can be found on
 @uref{https://tree-sitter.github.io/tree-sitter, tree-sitter's
 homepage}.
@@ -350,7 +356,7 @@ makes any node matched by @code{preprocessor_call_exp} 
appear as
 @end table
 
 Below are grammar functions of lesser importance for reading a
-language definition.
+language grammar.
 
 @table @code
 @item token(@var{rule})
@@ -397,7 +403,7 @@ when deciding whether to enable tree-sitter features.
 @cindex tree-sitter parser, creating
 @defun treesit-parser-create language &optional buffer no-reuse
 Create a parser for the specified @var{buffer} and @var{language}
-(@pxref{Language Definitions}).  If @var{buffer} is omitted or
+(@pxref{Language Grammar}).  If @var{buffer} is omitted or
 @code{nil}, it stands for the current buffer.
 
 By default, this function reuses a parser if one already exists for
@@ -685,7 +691,7 @@ This function finds the previous sibling of @var{node}.  If
 @cindex nodes, by field name
 @cindex syntax tree nodes, by field name
 
-To make the syntax tree easier to analyze, many language definitions
+To make the syntax tree easier to analyze, many language grammar
 assign @dfn{field names} to child nodes (@pxref{tree-sitter node field
 name, field name}).  For example, a @code{function_definition} node
 could have a @code{declarator} node and a @code{body} node.
@@ -929,7 +935,7 @@ tree.
 
 In general, nodes in a concrete syntax tree fall into two categories:
 @dfn{named nodes} and @dfn{anonymous nodes}.  Whether a node is named
-or anonymous is determined by the language definition
+or anonymous is determined by the language grammar
 (@pxref{tree-sitter named node, named node}).
 
 @cindex tree-sitter missing node
@@ -968,7 +974,7 @@ Named nodes have ``types'' (@pxref{tree-sitter node type, 
node type}).
 For example, a named node can be a @code{string_literal} node, where
 @code{string_literal} is its type.  The type of an anonymous node is
 just the text that the node represents; e.g., the type of a @samp{,}
-node 480is just @samp{,}.
+node is just @samp{,}.
 
 This function returns @var{node}'s type as a string.
 @end defun
@@ -1704,8 +1710,8 @@ whether tree-sitter can be activated in this mode.
 This function checks for conditions for activating tree-sitter.  It
 checks whether Emacs was built with tree-sitter, whether the buffer's
 size is not too large for tree-sitter to handle it, and whether the
-language definition for @var{language} is available on the system
-(@pxref{Language Definitions}).
+language grammar for @var{language} is available on the system
+(@pxref{Language Grammar}).
 
 This function emits a warning if tree-sitter cannot be activated.  If
 @var{quiet} is @code{message}, the warning is turned into a message;
@@ -1826,7 +1832,7 @@ Using (row, column) coordinates as position.
 Updating a node with changes.  (In Emacs, retrieve a new node instead
 of updating the existing one.)
 @item
-Querying statics of a language definition.
+Querying statics of a language grammar.
 @end itemize
 
 In addition, Emacs makes some changes to the C API to make the API more
diff --git a/etc/NEWS.29 b/etc/NEWS.29
index d2e11e5adf..38b7865626 100644
--- a/etc/NEWS.29
+++ b/etc/NEWS.29
@@ -2832,7 +2832,11 @@ project-dedicated or global) is specified by the new
 *** Support for endless methods.
 
 ---
-*** New user option 'ruby-method-params-indent'.
+*** New user options that determine indentation logic.
+'ruby-method-params-indent', 'ruby-block-indent',
+'ruby-after-operator-indent', 'ruby-method-call-indent',
+'ruby-parenless-call-arguments-indent'.  See the docstrings for
+explanations and examples.
 
 ** Eshell
 
diff --git a/lisp/eshell/em-tramp.el b/lisp/eshell/em-tramp.el
index 499deaa7fc..2453af1bac 100644
--- a/lisp/eshell/em-tramp.el
+++ b/lisp/eshell/em-tramp.el
@@ -30,8 +30,9 @@
 
 (eval-when-compile
   (require 'esh-mode)
-  (require 'eshell)
-  (require 'tramp))
+  (require 'eshell))
+
+(require 'tramp)
 
 ;; There are no items in this custom group, but eshell modules (ab)use
 ;; custom groups.
diff --git a/lisp/faces.el b/lisp/faces.el
index fe683e437f..0dd89be473 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -691,8 +691,8 @@ what the FACE's face spec says, call this function with 
FRAME set to
 t and the ATTRIBUTE's value set to `unspecified'.
 
 Note that the ATTRIBUTE VALUE pairs are evaluated in the order
-they are specified, except the `:family' and `:foundry'
-attributes which are evaluated first.
+they are specified, except that the `:family' and `:foundry'
+attributes are evaluated first.
 
 The following attributes are recognized:
 
diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
index 50b951888a..972a4d5aa5 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -595,7 +595,9 @@ ARG is passed to `fill-paragraph'."
            (start-marker nil)
            (end-marker nil)
            (end-len 0))
-      (when (equal (treesit-node-type node) "comment")
+      ;; These covers C/C++, Java, JavaScript, TypeScript, Rust, C#.
+      (when (member (treesit-node-type node)
+                    '("comment" "line_comment" "block_comment"))
         ;; We mask "/*" and the space before "*/" like
         ;; `c-fill-paragraph' does.
         (atomic-change-group
diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el
index a4aa61905e..3a86093941 100644
--- a/lisp/progmodes/ruby-mode.el
+++ b/lisp/progmodes/ruby-mode.el
@@ -285,6 +285,96 @@ Only has effect when `ruby-use-smie' is t."
   :safe (lambda (val) (or (memq val '(t nil)) (numberp val)))
   :version "29.1")
 
+(defcustom ruby-block-indent t
+  "Non-nil to align the body of a block to the statement's start.
+
+The body and the closer will be aligned to the column where the
+statement containing the block starts. Example:
+
+  foo.bar
+    .each do
+    baz
+  end
+
+If nil, it will be aligned instead to the beginning of the line
+containing the block's opener:
+
+  foo.bar
+    .each do
+      baz
+    end
+
+Only has effect when `ruby-use-smie' is t."
+  :type 'boolean
+  :safe 'booleanp
+  :version "29.1")
+
+(defcustom ruby-after-operator-indent t
+  "Non-nil to use structural indentation after binary operators.
+
+The code will be aligned to the implicit parent expression,
+according to the operator precedence:
+
+  qux = 4 + 5 *
+            6 +
+        7
+
+Set it to nil to align to the beginning of the statement:
+
+  qux = 4 + 5 *
+    6 +
+    7
+
+Only has effect when `ruby-use-smie' is t."
+  :type 'boolean
+  :safe 'booleanp
+  :version "29.1")
+
+(defcustom ruby-method-call-indent t
+  "Non-nil to use the structural indentation algorithm.
+
+The method call will be aligned to the implicit parent
+expression, according to the operator precedence:
+
+  foo = subject
+          .update(
+            1
+          )
+
+Set it to nil to align to the beginning of the statement:
+
+  foo = subject
+    .update(
+      1
+    )
+
+Only has effect when `ruby-use-smie' is t."
+  :type 'boolean
+  :safe 'booleanp
+  :version "29.1")
+
+(defcustom ruby-parenless-call-arguments-indent t
+  "Non-nil to align arguments in a parenless call vertically.
+
+Example:
+
+  qux :+,
+      bar,
+      :[]=,
+      bar
+
+Set it to nil to align to the beginning of the statement:
+
+  qux :+,
+    bar,
+    :[]=,
+    bar
+
+Only has effect when `ruby-use-smie' is t."
+  :type 'boolean
+  :safe 'booleanp
+  :version "29.1")
+
 (defcustom ruby-deep-arglist t
   "Deep indent lists in parenthesis when non-nil.
 Also ignores spaces after parenthesis when `space'.
@@ -416,6 +506,7 @@ This only affects the output of the command 
`ruby-toggle-block'."
      '((right "=")
        (right "+=" "-=" "*=" "/=" "%=" "**=" "&=" "|=" "^="
               "<<=" ">>=" "&&=" "||=")
+       (right "?")
        (nonassoc ".." "...")
        (left "&&" "||")
        (nonassoc "<=>")
@@ -608,10 +699,10 @@ This only affects the output of the command 
`ruby-toggle-block'."
           "def=")
          (t tok)))))))
 
-(defun ruby-smie--indent-to-stmt ()
+(defun ruby-smie--indent-to-stmt (&optional offset)
   (save-excursion
     (smie-backward-sexp ";")
-    (cons 'column (smie-indent-virtual))))
+    (cons 'column (+ (smie-indent-virtual) (or offset 0)))))
 
 (defun ruby-smie--indent-to-stmt-p (keyword)
   (or (eq t ruby-align-to-stmt-keywords)
@@ -642,7 +733,9 @@ This only affects the output of the command 
`ruby-toggle-block'."
               (forward-comment -1)
               (not (eq (preceding-char) ?:))))
        ;; Curly block opener.
-       (ruby-smie--indent-to-stmt))
+       (if ruby-block-indent
+           (ruby-smie--indent-to-stmt)
+         (cons 'column (current-indentation))))
       ((smie-rule-hanging-p)
        ;; Treat purely syntactic block-constructs as being part of their 
parent,
        ;; when the opening token is hanging and the parent is not an
@@ -677,13 +770,20 @@ This only affects the output of the command 
`ruby-toggle-block'."
        (unless (or (eolp) (forward-comment 1))
          (cons 'column (current-column)))))
     ('(:before . " @ ")
-     (if (or (eq ruby-method-params-indent t)
-             (not (smie-rule-parent-p "def" "def=")))
-         (save-excursion
-           (skip-chars-forward " \t")
-           (cons 'column (current-column)))
-       (smie-rule-parent (or ruby-method-params-indent 0))))
-    ('(:before . "do") (ruby-smie--indent-to-stmt))
+     (cond
+      ((and (not ruby-parenless-call-arguments-indent)
+            (not (smie-rule-parent-p "def" "def=")))
+       (ruby-smie--indent-to-stmt ruby-indent-level))
+      ((or (eq ruby-method-params-indent t)
+           (not (smie-rule-parent-p "def" "def=")))
+       (save-excursion
+         (skip-chars-forward " \t")
+         (cons 'column (current-column))))
+      (t (smie-rule-parent (or ruby-method-params-indent 0)))))
+    ('(:before . "do")
+     (if ruby-block-indent
+         (ruby-smie--indent-to-stmt)
+       (cons 'column (current-indentation))))
     ('(:before . ".")
      (if (smie-rule-sibling-p)
          (when ruby-align-chained-calls
@@ -696,8 +796,10 @@ This only affects the output of the command 
`ruby-toggle-block'."
                    (not (smie-rule-bolp)))))
            (cons 'column (current-column)))
        (smie-backward-sexp ".")
-       (cons 'column (+ (current-column)
-                        ruby-indent-level))))
+       (if ruby-method-call-indent
+           (cons 'column (+ (current-column)
+                            ruby-indent-level))
+         (ruby-smie--indent-to-stmt ruby-indent-level))))
     (`(:before . ,(or "else" "then" "elsif" "rescue" "ensure"))
      (smie-rule-parent))
     (`(:before . ,(or "when" "in"))
@@ -708,16 +810,22 @@ This only affects the output of the command 
`ruby-toggle-block'."
                      "<=>" ">" "<" ">=" "<=" "==" "===" "!=" "<<" ">>"
                      "+=" "-=" "*=" "/=" "%=" "**=" "&=" "|=" "^=" "|"
                      "<<=" ">>=" "&&=" "||=" "and" "or"))
-     (and (smie-rule-parent-p ";" nil)
-          (smie-indent--hanging-p)
-          ruby-indent-level))
+     (cond
+      ((not ruby-after-operator-indent)
+       (ruby-smie--indent-to-stmt ruby-indent-level))
+      ((and (smie-rule-parent-p ";" nil)
+            (smie-indent--hanging-p))
+       ruby-indent-level)))
     (`(:before . "=")
      (save-excursion
       (and (smie-rule-parent-p " @ ")
            (goto-char (nth 1 (smie-indent--parent)))
            (smie-rule-prev-p "def=")
            (cons 'column (+ (current-column) ruby-indent-level -3)))))
-    (`(:after . ,(or "?" ":")) ruby-indent-level)
+    (`(:after . ,(or "?" ":"))
+     (if ruby-after-operator-indent
+         ruby-indent-level
+       (ruby-smie--indent-to-stmt ruby-indent-level)))
     (`(:before . ,(guard (memq (intern-soft token) ruby-alignable-keywords)))
      (when (not (ruby--at-indentation-p))
        (if (ruby-smie--indent-to-stmt-p token)
@@ -725,7 +833,10 @@ This only affects the output of the command 
`ruby-toggle-block'."
          (cons 'column (current-column)))))
     ('(:before . "iuwu-mod")
      (smie-rule-parent ruby-indent-level))
-    ))
+    (`(:before . ",")
+     (and (not ruby-parenless-call-arguments-indent)
+          (smie-rule-parent-p " @ ")
+          (ruby-smie--indent-to-stmt ruby-indent-level)))))
 
 (defun ruby--at-indentation-p (&optional point)
   (save-excursion
diff --git a/lisp/treesit.el b/lisp/treesit.el
index 4af555fb8e..8f6354fde2 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -36,6 +36,7 @@
 (eval-when-compile (require 'subr-x)) ; For `string-join'.
 (require 'cl-seq)
 (require 'font-lock)
+(require 'seq)
 
 ;;; Function declarations
 
@@ -2163,7 +2164,7 @@ instead of emitting a warning."
         (pcase-let ((`(,available . ,err)
                      (treesit-language-available-p lang t)))
           (when (not available)
-            (setq msg (format "language definition for %s is unavailable (%s): 
%s"
+            (setq msg (format "language grammar for %s is unavailable (%s): %s"
                               lang (nth 0 err)
                               (string-join
                                (mapcar (lambda (x) (format "%s" x))
@@ -2310,7 +2311,7 @@ When this mode is enabled, the mode-line displays
 where NODE, CHILD, etc, are nodes which begin at point.  PARENT
 is the parent of NODE.  NODE is displayed in bold typeface.
 FIELD-NAMEs are field names of NODE and CHILD, etc (see Info
-node `(elisp)Language Definitions', heading \"Field names\").
+node `(elisp)Language Grammar', heading \"Field names\").
 
 If no node starts at point, i.e., point is in the middle of a
 node, then the mode line displays the earliest node that spans point,
@@ -2649,6 +2650,143 @@ window."
                  #'treesit--explorer-post-command t)
     (kill-buffer treesit--explorer-buffer)))
 
+;;; Install & build language grammar
+
+(defvar treesit-language-source-alist nil
+  "Configures how to download tree-sitter language grammars.
+This should be an alist of
+
+    (LANG . (URL SOURCE-DIR GRAMMAR-DIR CC C++))
+
+Only LANG and URL are mandatory.  LANG is the language symbol.
+URL is the repository's url.
+
+SOURCE-DIR is the relative directory in the repository in which
+the grammar.c file resides, default to \"src\".
+
+GRAMMAR-DIR is the relative grammar directory in the repository
+in which the grammar.js file resides, default to \"\".
+
+CC and C++ are C and C++ compilers, default to \"cc\" and
+\"c++\", respectively.")
+
+(defun treesit-install-language-grammar (lang)
+  "Build and install the tree-sitter language grammar library for LANG.
+
+This command requires Git, a C compiler and (sometimes) a C++ compiler,
+and the linker to be installed and on PATH.  It also requires that the
+recipe for LANG exists in `treesit-language-source-alist'.
+
+See `exec-path' for the current path where Emacs looks for
+executable programs, such as the C/C++ compiler and linker."
+  (interactive (list (intern
+                      (completing-read
+                       "Language: "
+                       (mapcar #'car treesit-language-source-alist)
+                       nil t))))
+  (condition-case err
+      (apply #'treesit--install-language-grammar-1
+             ;; The nil is OUT-DIR.
+             (cons nil (assoc lang treesit-language-source-alist)))
+    (error
+     (display-warning
+      'treesit
+      (format "Error encountered when installing language grammar: %s"
+              err))))
+  ;; Check that the installed language grammar is loadable.
+  (pcase-let ((`(,available . ,err)
+               (treesit-language-available-p lang t)))
+    (when (not available)
+      (display-warning
+       'treesit
+       (format "The installed language grammar for %s cannot be located or has 
problems (%s): %s"
+               lang (nth 0 err)
+               (string-join
+                (mapcar (lambda (x) (format "%s" x))
+                        (cdr err))
+                " "))))))
+
+(defun treesit--call-process-signal (&rest args)
+  "Run `call-process' with ARGS.
+If it returns anything but 0, signal an error.  Use the buffer
+content as signal data, and erase buffer afterwards."
+  (unless (eq 0 (apply #'call-process args))
+    (signal 'treesit-error (list "Command:"
+                                 (string-join (cons (car args)
+                                                    (nthcdr 4 args))
+                                              " ")
+                                 "Error output:"
+                                 (buffer-string)))
+    (erase-buffer)))
+
+(defun treesit--install-language-grammar-1
+    (out-dir lang url &optional source-dir grammar-dir cc c++)
+  "Install and compile a tree-sitter language grammar library.
+
+OUT-DIR is the directory to put the compiled library file.  If it
+is nil, the \"tree-sitter\" directory under user's Emacs
+configuration directory is used (and automatically created if not
+exist).
+
+For LANG, URL, SOURCE-DIR, GRAMMAR-DIR, CC, C++, see
+`treesit-language-source-alist'.  If anything goes wrong, this
+function signals an error."
+  (let* ((lang (symbol-name lang))
+         (default-directory (make-temp-file "treesit-workdir" t))
+         (workdir (expand-file-name "repo"))
+         (source-dir (expand-file-name (or source-dir "src") workdir))
+         (grammar-dir (expand-file-name (or grammar-dir "") workdir))
+         (cc (or cc (seq-find #'executable-find '("cc" "gcc" "c99"))
+                 ;; If no C compiler found, just use cc and let
+                 ;; `call-process' signal the error.
+                 "cc"))
+         (c++ (or c++ (seq-find #'executable-find '("c++" "g++"))
+                  "c++"))
+         (soext (or (car dynamic-library-suffixes)
+                    (signal 'treesit-error '("Emacs cannot figure out the file 
extension for dynamic libraries for this system, because 
`dynamic-library-suffixes' is nil"))))
+         (out-dir (or (and out-dir (expand-file-name out-dir))
+                      (locate-user-emacs-file "tree-sitter")))
+         (lib-name (concat "libtree-sitter-" lang soext)))
+    (unwind-protect
+        (with-temp-buffer
+          (message "Cloning repository")
+          ;; git clone xxx --depth 1 --quiet workdir
+          (treesit--call-process-signal
+           "git" nil t nil "clone" url "--depth" "1" "--quiet"
+           workdir)
+          ;; cp "${grammardir}"/grammar.js "${sourcedir}"
+          (copy-file (expand-file-name "grammar.js" grammar-dir)
+                     (expand-file-name "grammar.js" source-dir)
+                     t t)
+          ;; cd "${sourcedir}"
+          (setq default-directory source-dir)
+          (message "Compiling library")
+          ;; cc -fPIC -c -I. parser.c
+          (treesit--call-process-signal
+           cc nil t nil "-fPIC" "-c" "-I." "parser.c")
+          ;; cc -fPIC -c -I. scanner.c
+          (when (file-exists-p "scanner.c")
+            (treesit--call-process-signal
+             cc nil t nil "-fPIC" "-c" "-I." "scanner.c"))
+          ;; c++ -fPIC -I. -c scanner.cc
+          (when (file-exists-p "scanner.cc")
+            (treesit--call-process-signal
+             c++ nil t nil "-fPIC" "-c" "-I." "scanner.cc"))
+          ;; cc/c++ -fPIC -shared *.o -o "libtree-sitter-${lang}.${soext}"
+          (apply #'treesit--call-process-signal
+                 (if (file-exists-p "scanner.cc") c++ cc)
+                 nil t nil
+                 `("-fPIC" "-shared"
+                   ,@(directory-files
+                      default-directory nil
+                      (rx bos (+ anychar) ".o" eos))
+                   "-o" ,lib-name))
+          ;; Copy out.
+          (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)
+        (delete-directory workdir t)))))
+
 ;;; Etc
 
 (declare-function find-library-name "find-func.el")
diff --git a/src/treesit.c b/src/treesit.c
index eaa563a54c..974d2fc451 100644
--- a/src/treesit.c
+++ b/src/treesit.c
@@ -662,9 +662,8 @@ If DETAIL is non-nil, return (t . nil) when LANGUAGE is 
available,
     }
 }
 
-DEFUN ("treesit-language-version",
-       Ftreesit_language_version,
-       Streesit_language_version,
+DEFUN ("treesit-library-abi-version", Ftreesit_library_abi_version,
+       Streesit_library_abi_version,
        0, 1, 0,
        doc: /* Return the language ABI version of the tree-sitter library.
 
@@ -680,6 +679,29 @@ is non-nil, return the oldest compatible ABI version.  */)
     return make_fixnum (TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION);
 }
 
+DEFUN ("treesit-language-version", Ftreesit_language_abi_version,
+       Streesit_language_abi_version,
+       0, 1, 0,
+       doc: /* Return the language ABI version of the tree-sitter LANGUAGE.
+Return nil if LANGUAGE is not available.  */)
+  (Lisp_Object language)
+{
+  if (NILP (Ftreesit_langauge_available_p (language, Qnil)))
+    return Qnil;
+  else
+    {
+      Lisp_Object signal_symbol = Qnil;
+      Lisp_Object signal_data = Qnil;
+      TSLanguage *ts_language = treesit_load_language (language,
+                                                      &signal_symbol,
+                                                      &signal_data);
+      if (ts_language == NULL)
+       return Qnil;
+      uint32_t version =  ts_language_version (ts_language);
+      return make_fixnum((ptrdiff_t) version);
+    }
+}
+
 /*** Parsing functions */
 
 static void
@@ -1172,10 +1194,12 @@ treesit_query_error_to_string (TSQueryError error)
 
 static Lisp_Object
 treesit_compose_query_signal_data (uint32_t error_offset,
-                                  TSQueryError error_type)
+                                  TSQueryError error_type,
+                                  Lisp_Object query_source)
 {
-  return list3 (build_string (treesit_query_error_to_string (error_type)),
+  return list4 (build_string (treesit_query_error_to_string (error_type)),
                make_fixnum (error_offset + 1),
+               query_source,
                build_pure_c_string ("Debug the query with 
`treesit-query-validate'"));
 }
 
@@ -1217,7 +1241,8 @@ treesit_ensure_query_compiled (Lisp_Object query, 
Lisp_Object *signal_symbol,
     {
       *signal_symbol = Qtreesit_query_error;
       *signal_data = treesit_compose_query_signal_data (error_offset,
-                                                       error_type);
+                                                       error_type,
+                                                       source);
     }
   XTS_COMPILED_QUERY (query)->query = treesit_query;
   return treesit_query;
@@ -2605,7 +2630,7 @@ the query.  */)
       if (treesit_query == NULL)
        xsignal (Qtreesit_query_error,
                 treesit_compose_query_signal_data (error_offset,
-                                                   error_type));
+                                                   error_type, query));
       cursor = ts_query_cursor_new ();
       needs_to_free_query_and_cursor = true;
     }
@@ -3345,7 +3370,8 @@ then in the system default locations for dynamic 
libraries, in that order.  */);
   Vtreesit_extra_load_path = Qnil;
 
   defsubr (&Streesit_language_available_p);
-  defsubr (&Streesit_language_version);
+  defsubr (&Streesit_library_abi_version);
+  defsubr (&Streesit_language_abi_version);
 
   defsubr (&Streesit_parser_p);
   defsubr (&Streesit_node_p);
diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el
index d7f4576335..50b0e948e4 100644
--- a/test/lisp/net/tramp-tests.el
+++ b/test/lisp/net/tramp-tests.el
@@ -5510,15 +5510,11 @@ INPUT, if non-nil, is a string sent to the process."
               ;; String to be sent.
               (format "%s\n" (file-name-nondirectory tmp-name)))
              (should
-              (string-equal
-               ;; tramp-adb.el echoes, so we must add the string.
-               (if (and (tramp--test-adb-p)
-                        (not (tramp-direct-async-process-p)))
-                   (format
-                    "%s\n%s\n"
-                    (file-name-nondirectory tmp-name)
-                    (file-name-nondirectory tmp-name))
-                 (format "%s\n" (file-name-nondirectory tmp-name)))
+              (string-match-p
+               ;; Some shells echo, for example the "adb" or "docker" methods.
+               (tramp-compat-rx
+                bos (** 1 2 (literal (file-name-nondirectory tmp-name)) "\n")
+                eos)
                (buffer-string))))
 
          ;; Cleanup.
diff --git 
a/test/lisp/progmodes/ruby-mode-resources/ruby-after-operator-indent.rb 
b/test/lisp/progmodes/ruby-mode-resources/ruby-after-operator-indent.rb
new file mode 100644
index 0000000000..25cd8736f9
--- /dev/null
+++ b/test/lisp/progmodes/ruby-mode-resources/ruby-after-operator-indent.rb
@@ -0,0 +1,29 @@
+4 +
+  5 +
+  6 +
+  7
+
+qux = 4 + 5 *
+  6 +
+  7
+
+foo = obj.bar { |m| tee(m) } +
+  obj.qux { |m| hum(m) }
+
+foo.
+  bar
+  .baz
+
+qux = foo.fee ?
+  bar :
+  tee
+
+# Endless methods.
+class Bar
+  def foo(abc) = bar +
+    baz
+end
+
+# Local Variables:
+# ruby-after-operator-indent: nil
+# End:
diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby-block-indent.rb 
b/test/lisp/progmodes/ruby-mode-resources/ruby-block-indent.rb
new file mode 100644
index 0000000000..32882814b7
--- /dev/null
+++ b/test/lisp/progmodes/ruby-mode-resources/ruby-block-indent.rb
@@ -0,0 +1,33 @@
+foo
+  .asdasd
+  .proc do |**args|
+    p(**args)
+  end
+
+foo
+  .asdasd
+  .proc { |**args|
+    p(**args)
+  }
+
+bar.foo do
+  bar
+end
+
+bar.foo(tee) do
+  bar
+end
+
+bar.foo(tee) {
+  bar
+}
+
+x.foo do
+  foo
+end.bar do
+  bar
+end
+
+# Local Variables:
+# ruby-block-indent: nil
+# End:
diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby-method-call-indent.rb 
b/test/lisp/progmodes/ruby-mode-resources/ruby-method-call-indent.rb
new file mode 100644
index 0000000000..1a8285ee91
--- /dev/null
+++ b/test/lisp/progmodes/ruby-mode-resources/ruby-method-call-indent.rb
@@ -0,0 +1,15 @@
+foo2 =
+  subject.
+  update(
+    2
+  )
+
+foo3 =
+  subject
+  .update(
+    2
+  )
+
+# Local Variables:
+# ruby-method-call-indent: nil
+# End:
diff --git 
a/test/lisp/progmodes/ruby-mode-resources/ruby-parenless-call-arguments-indent.rb
 
b/test/lisp/progmodes/ruby-mode-resources/ruby-parenless-call-arguments-indent.rb
new file mode 100644
index 0000000000..58e08810c4
--- /dev/null
+++ 
b/test/lisp/progmodes/ruby-mode-resources/ruby-parenless-call-arguments-indent.rb
@@ -0,0 +1,23 @@
+method arg1,
+  method2 arg2,
+  arg3, [
+    arg4,
+    arg5
+  ]
+
+zzz = method (a + b),
+  c, :d => :e,
+  f: g
+
+return render json: {
+    errors: { base: [message] },
+    copying: copying
+  },
+  status: 400
+
+foo(a,
+    b)
+
+# Local Variables:
+# ruby-parenless-call-arguments-indent: nil
+# End:
diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby.rb 
b/test/lisp/progmodes/ruby-mode-resources/ruby.rb
index 6a69d9db78..bfae948b25 100644
--- a/test/lisp/progmodes/ruby-mode-resources/ruby.rb
+++ b/test/lisp/progmodes/ruby-mode-resources/ruby.rb
@@ -226,6 +226,7 @@ desc "foo foo" \
 
 foo.
   bar
+  .baz
 
 # 
https://github.com/rails/rails/blob/17f5d8e062909f1fcae25351834d8e89967b645e/activesupport/lib/active_support/time_with_zone.rb#L206
 foo # comment intended to confuse the tokenizer
@@ -380,6 +381,18 @@ foo = [1, 2, 3].map do |i|
   i + 1
 end
 
+m1 = foo
+       .asdasd
+       .proc do |**args|
+  p(**args)
+end
+
+m2 = foo
+       .asdasd
+       .proc { |**args|
+  p(**args)
+}
+
 bar.foo do
   bar
 end
@@ -398,6 +411,12 @@ bar 1 do
   end
 end
 
+x.foo do
+  foo
+end.bar do
+  bar
+end
+
 foo |
   bar
 
@@ -540,5 +559,9 @@ class Bar
 end
 
 # Local Variables:
+# ruby-after-operator-indent: t
+# ruby-block-indent: t
+# ruby-method-call-indent: t
 # ruby-method-params-indent: t
+# ruby-parenless-call-arguments-indent: t
 # End:
diff --git a/test/lisp/progmodes/ruby-mode-tests.el 
b/test/lisp/progmodes/ruby-mode-tests.el
index 560f780285..5c81cc31cc 100644
--- a/test/lisp/progmodes/ruby-mode-tests.el
+++ b/test/lisp/progmodes/ruby-mode-tests.el
@@ -956,7 +956,11 @@ VALUES-PLIST is a list with alternating index and value 
elements."
          (kill-buffer buf)))))
 
 (ruby-deftest-indent "ruby.rb")
+(ruby-deftest-indent "ruby-after-operator-indent.rb")
+(ruby-deftest-indent "ruby-block-indent.rb")
+(ruby-deftest-indent "ruby-method-call-indent.rb")
 (ruby-deftest-indent "ruby-method-params-indent.rb")
+(ruby-deftest-indent "ruby-parenless-call-arguments-indent.rb")
 
 (ert-deftest ruby--test-chained-indentation ()
   (with-temp-buffer



reply via email to

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