emacs-diffs
[Top][All Lists]
Advanced

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

master 6d6ca47aba7 3/7: Merge from origin/emacs-29


From: Po Lu
Subject: master 6d6ca47aba7 3/7: Merge from origin/emacs-29
Date: Sat, 10 Dec 2022 20:34:33 -0500 (EST)

branch: master
commit 6d6ca47aba7b72d2a770d7ed01c849d3cc729e21
Merge: e0856443291 44c5f361497
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    Merge from origin/emacs-29
    
    44c5f361497 ; Fix two byte-compiler warnings
    a8ee046fb50 Ensure 'package-vc--version' always returns a version
    022ab1061b2 Ensure 'package-vc--main-file' always returns an existing...
    357fe91996b Check if package already exists before installing from ch...
    5e8bc79f6b2 ; Fix reference in docstring to 'package-vc-install-from-...
    af88b00b19c Refresh the package quickstart file in package-vc
    5a092c8e461 ; * admin/notes/tree-sitter/starter-guide (Indent): Minor...
    ebef8905b0d Make indirect buffers use tree-sitter parsers of their ba...
    8f53fa10d94 Fontify "this" as a keyword in c++-ts-mode (bug#59924)
    8de8f1dc051 Add class_body indentation for typescript (bug#59680)
    839341d7370 Make more granular defun-type-regexp (bug#59873)
    8f49137c9bf Add dockerfile-ts-mode (Bug#59894)
    1014bcc8e32 Fix fontification of method-invocations in js-ts-mode (bu...
    7141920c6af Fix escape-sequence feature in typescript-ts-mode (bug#59...
    4df35e3491c Improve fontification in csharp-ts-mode (bug#59909)
    33a8415eb7e Use 'project--value-in-dir' for 'project-vc-include-untra...
    594267395d5 Update Turkish Hello
    940d9070e97 Support newer glib versions (Bug#59061)
    0bd26abf7fb ; * doc/misc/use-package.texi: Fix @file.
    bcf235acd58 Merge branch 'emacs-29' of git.savannah.gnu.org:/srv/git/...
    2ea7a357fd1 ; * doc/misc/use-package.texi: Fix @acronym.
    d268ab1c5d7 Bring back the project--value-in-dir logic
---
 admin/notes/tree-sitter/build-module/batch.sh      |   1 +
 admin/notes/tree-sitter/build-module/build.sh      |   6 +-
 admin/notes/tree-sitter/starter-guide              |   2 +-
 doc/lispref/parsing.texi                           |  10 +-
 doc/misc/use-package.texi                          |   4 +-
 etc/HELLO                                          |   2 +-
 etc/NEWS                                           |   5 +
 lib-src/seccomp-filter.c                           |   2 +
 lisp/emacs-lisp/package-vc.el                      |  56 +++++--
 lisp/gnus/gnus-icalendar.el                        |   2 +-
 lisp/language/european.el                          |   2 +-
 lisp/progmodes/c-ts-mode.el                        |   6 +-
 lisp/progmodes/csharp-mode.el                      |  10 +-
 lisp/progmodes/dockerfile-ts-mode.el               | 176 +++++++++++++++++++++
 lisp/progmodes/eglot.el                            |   2 +-
 lisp/progmodes/java-ts-mode.el                     |  10 +-
 lisp/progmodes/js.el                               |  27 ++--
 lisp/progmodes/project.el                          |  49 ++++--
 lisp/progmodes/typescript-ts-mode.el               |   7 +-
 src/treesit.c                                      |  61 +++++--
 .../progmodes/project-resources/.dir-locals.el     |   1 +
 test/lisp/progmodes/project-resources/etc          |   1 +
 test/lisp/progmodes/project-resources/foo          |   1 +
 test/lisp/progmodes/project-tests.el               |  15 +-
 test/src/treesit-tests.el                          |  34 ++++
 25 files changed, 413 insertions(+), 79 deletions(-)

diff --git a/admin/notes/tree-sitter/build-module/batch.sh 
b/admin/notes/tree-sitter/build-module/batch.sh
index d45f37f4b64..6dce000caa6 100755
--- a/admin/notes/tree-sitter/build-module/batch.sh
+++ b/admin/notes/tree-sitter/build-module/batch.sh
@@ -5,6 +5,7 @@ languages=(
     'cpp'
     'css'
     'c-sharp'
+    'dockerfile'
     'go'
     'html'
     'javascript'
diff --git a/admin/notes/tree-sitter/build-module/build.sh 
b/admin/notes/tree-sitter/build-module/build.sh
index d562f1a7846..cc31e3f6f02 100755
--- a/admin/notes/tree-sitter/build-module/build.sh
+++ b/admin/notes/tree-sitter/build-module/build.sh
@@ -14,11 +14,15 @@ echo "Building ${lang}"
 
 ### Retrieve sources
 
+namespace="tree-sitter"
 repo="tree-sitter-${lang}"
 sourcedir="tree-sitter-${lang}/src"
 grammardir="tree-sitter-${lang}"
 
 case "${lang}" in
+    "dockerfile")
+        namespace="camdencheek"
+        ;;
     "typescript")
         sourcedir="tree-sitter-typescript/typescript/src"
         grammardir="tree-sitter-typescript/typescript"
@@ -30,7 +34,7 @@ case "${lang}" in
         ;;
 esac
 
-git clone "https://github.com/tree-sitter/${repo}.git"; \
+git clone "https://github.com/${namespace}/${repo}.git"; \
     --depth 1 --quiet
 cp "${grammardir}"/grammar.js "${sourcedir}"
 # We have to go into the source directory to compile, because some
diff --git a/admin/notes/tree-sitter/starter-guide 
b/admin/notes/tree-sitter/starter-guide
index 123dabd9f29..a6a4c647f23 100644
--- a/admin/notes/tree-sitter/starter-guide
+++ b/admin/notes/tree-sitter/starter-guide
@@ -282,7 +282,7 @@ For MATHCER we have
            NODE-INDEX-MIN NODE-INDEX-MAX)
 
     => checks everything. If an argument is nil, don’t match that. Eg,
-    (match nil nil TYPE) is the same as (parent-is TYPE)
+    (match nil TYPE) is the same as (parent-is TYPE)
 
 For ANCHOR we have
 
diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi
index 3223875320a..af7be2ebf36 100644
--- a/doc/lispref/parsing.texi
+++ b/doc/lispref/parsing.texi
@@ -409,6 +409,13 @@ Create a parser for the specified @var{buffer} and 
@var{language}
 By default, this function reuses a parser if one already exists for
 @var{language} in @var{buffer}, but if @var{no-reuse} is
 non-@code{nil}, this function always creates a new parser.
+
+If that buffer is an indirect buffer, its base buffer is used instead.
+That is, indirect buffers use their base buffer's parsers.  If the
+base buffer is narrowed, an indirect buffer might not be able to
+retrieve information of the portion of the buffer text that are
+invisible in the base buffer.  Lisp programs should widen as necessary
+should they want to use a parser in an indirect buffer.
 @end defun
 
 Given a parser, we can query information about it.
@@ -447,7 +454,8 @@ tree incrementally.
 @defun treesit-parser-list &optional buffer
 This function returns the parser list of @var{buffer}.  If
 @var{buffer} is @code{nil} or omitted, it defaults to the current
-buffer.
+buffer.  If that buffer is an indirect buffer, its base buffer is used
+instead.  That is, indirect buffers use their base buffer's parsers.
 @end defun
 
 @defun treesit-parser-delete parser
diff --git a/doc/misc/use-package.texi b/doc/misc/use-package.texi
index daf27ec5bbc..d3b6ee99003 100644
--- a/doc/misc/use-package.texi
+++ b/doc/misc/use-package.texi
@@ -190,7 +190,7 @@ immediately.  In most cases, this is not necessary or 
desirable, as
 that will slow down Emacs startup.  Instead, you should try to set
 things up so that packages are only loaded when they are actually
 needed (a.k.a. ``autoloading'').  If you have installed a package from
-@acronym{GNU ELPA} that provides it's own autoloads, it is often
+@acronym{GNU} @acronym{ELPA} that provides it's own autoloads, it is often
 enough to say:
 
 @lisp
@@ -1273,7 +1273,7 @@ cells, or a string or regexp.
 The following example reproduces the default @code{ruby-mode}
 configuration, exactly as it is in Emacs out-of-the-box.  That mode is
 enabled automatically when a file whose name matches the regexp
-@code{"\\.rb\\'"} (a file with the @samp{.rb} extension), or when the
+@code{"\\.rb\\'"} (a file with the @file{.rb} extension), or when the
 first line of the file (known as the ``shebang'') matches the string
 @code{"ruby"}:
 
diff --git a/etc/HELLO b/etc/HELLO
index 7bc12063f8e..fbe65a451e1 100644
--- a/etc/HELLO
+++ b/etc/HELLO
@@ -114,7 +114,7 @@ Thai (ภาษาไทย)      สวัสดีครับ / สวัสดี
 Tibetan (བོད་སྐད་)     བཀྲ་ཤིས་བདེ་ལེགས༎
 Tigrigna (ትግርኛ)        ሰላማት
 Tirhuta (𑒞𑒱𑒩𑒯𑒳𑒞𑒰)      𑒣𑓂𑒩𑒢𑒰𑒧 / 𑒮𑒲𑒞𑒰𑒩𑒰𑒧
-Turkish (Türkçe)       Merhaba
+Turkish (Türkçe)       Esenlikler / Merhaba
 Ukrainian (українська) Вітаю / Добрий день! / Привіт
 Vietnamese (tiếng Việt)        Chào bạn
 Wancho (𞋒𞋀𞋉𞋃𞋕)         𞋂𞋈𞋛
diff --git a/etc/NEWS b/etc/NEWS
index 0c1fdfc454b..3338c06f037 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -79,6 +79,11 @@ After manually editing 'eshell-aliases-file', you can use
 
 * New Modes and Packages in Emacs 30.1
 
+** New major mode 'dockerfile-ts-mode'.
+A major mode based on the tree-sitter library for editing
+Dockerfiles.  It includes support for font-locking, indentation, Imenu,
+and which-func.
+
 
 * Incompatible Lisp Changes in Emacs 30.1
 
diff --git a/lib-src/seccomp-filter.c b/lib-src/seccomp-filter.c
index 7e54b878a22..69b56aed5c5 100644
--- a/lib-src/seccomp-filter.c
+++ b/lib-src/seccomp-filter.c
@@ -342,6 +342,8 @@ main (int argc, char **argv)
   RULE (SCMP_ACT_ALLOW, SCMP_SYS (eventfd2));
   RULE (SCMP_ACT_ALLOW, SCMP_SYS (wait4));
   RULE (SCMP_ACT_ALLOW, SCMP_SYS (poll));
+  RULE (SCMP_ACT_ALLOW, SCMP_SYS (pidfd_open),
+       SCMP_A1_32 (SCMP_CMP_EQ, 0));
 
   /* Don't allow creating sockets (network access would be extremely
      dangerous), but also don't crash.  */
diff --git a/lisp/emacs-lisp/package-vc.el b/lisp/emacs-lisp/package-vc.el
index 299964c6924..db54e0e130e 100644
--- a/lisp/emacs-lisp/package-vc.el
+++ b/lisp/emacs-lisp/package-vc.el
@@ -50,6 +50,7 @@
 (eval-when-compile (require 'rx))
 (eval-when-compile (require 'inline))
 (eval-when-compile (require 'map))
+(eval-when-compile (require 'cl-lib))
 (require 'package)
 (require 'lisp-mnt)
 (require 'vc)
@@ -293,21 +294,41 @@ asynchronously."
         (insert-file-contents main-file)
         (package-strip-rcs-id
          (or (lm-header "package-version")
-             (lm-header "version"))))
+             (lm-header "version")
+             "0")))
     "0"))
 
 (defun package-vc--main-file (pkg-desc)
   "Return the name of the main file for PKG-DESC."
   (cl-assert (package-vc-p pkg-desc))
-  (let ((pkg-spec (package-vc--desc->spec pkg-desc))
-        (name (symbol-name (package-desc-name pkg-desc))))
-    (or (plist-get pkg-spec :main-file)
-        (expand-file-name
-         (concat name ".el")
-         (file-name-concat
-          (or (package-desc-dir pkg-desc)
-              (expand-file-name name package-user-dir))
-          (plist-get pkg-spec :lisp-dir))))))
+  (let* ((pkg-spec (package-vc--desc->spec pkg-desc))
+         (name (symbol-name (package-desc-name pkg-desc)))
+         (directory (file-name-concat
+                     (or (package-desc-dir pkg-desc)
+                         (expand-file-name name package-user-dir))
+                     (plist-get pkg-spec :lisp-dir)))
+         (file (or (plist-get pkg-spec :main-file)
+                   (expand-file-name
+                    (concat name ".el")
+                    directory))))
+    (if (file-exists-p file) file
+      ;; The following heuristic is only necessary when fetching a
+      ;; repository with URL that would break the above assumptions.
+      ;; Concrete example: https://github.com/sachac/waveform-el does
+      ;; not have a file waveform-el.el, but a file waveform.el, so we
+      ;; try and find the closest match.
+      (let ((distance most-positive-fixnum) (best nil))
+        (dolist (alt (directory-files directory t "\\.el\\'" t))
+          (let ((sd (string-distance file alt)))
+            (when (and (not (string-match-p (rx (or (: "-autoloads.el")
+                                                    (: "-pkg.el"))
+                                                eos)
+                                            alt))
+                       (< sd distance))
+              (when (< sd distance)
+                (setq distance (string-distance file alt)
+                      best alt)))))
+        best))))
 
 (defun package-vc--generate-description-file (pkg-desc pkg-file)
   "Generate a package description file for PKG-DESC and write it to PKG-FILE."
@@ -466,6 +487,7 @@ documentation and marking the package as installed."
   (package--save-selected-packages
    (cons (package-desc-name pkg-desc)
          package-selected-packages))
+  (package--quickstart-maybe-refresh)
 
   ;; Confirm that the installation was successful
   (let ((main-file (package-vc--main-file pkg-desc)))
@@ -710,11 +732,11 @@ regular package, but it will not remove a VC package."
 (defun package-vc-checkout (pkg-desc directory &optional rev)
   "Clone the sources for PKG-DESC into DIRECTORY and visit that directory.
 Unlike `package-vc-install', this does not yet set up the package
-for use with Emacs; use `package-vc-link-directory' for setting
-the package up after this function finishes.
-Optional argument REV means to clone a specific version of the
-package; it defaults to the last version available from the
-package's repository.  If REV has the special value
+for use with Emacs; use `package-vc-install-from-checkout' for
+setting the package up after this function finishes.  Optional
+argument REV means to clone a specific version of the package; it
+defaults to the last version available from the package's
+repository.  If REV has the special value
 `:last-release' (interactively, the prefix argument), that stands
 for the last released version of the package."
   (interactive
@@ -753,6 +775,10 @@ name from the base name of DIR."
   (package-vc--archives-initialize)
   (let* ((name (or name (file-name-base (directory-file-name dir))))
          (pkg-dir (expand-file-name name package-user-dir)))
+    (when (file-exists-p pkg-dir)
+      (if (yes-or-no-p (format "Overwrite previous checkout for package `%s'?" 
name))
+          (package--delete-directory pkg-dir)
+        (error "There already exists a checkout for %s" name)))
     (make-symbolic-link (expand-file-name dir) pkg-dir)
     (package-vc--unpack-1
      (package-desc-create
diff --git a/lisp/gnus/gnus-icalendar.el b/lisp/gnus/gnus-icalendar.el
index 1bffdf3513a..f3665c3f1a4 100644
--- a/lisp/gnus/gnus-icalendar.el
+++ b/lisp/gnus/gnus-icalendar.el
@@ -603,7 +603,7 @@ is searched."
     (when file
       (switch-to-buffer (find-file file))
       (goto-char (org-find-entry-with-id (gnus-icalendar-event:uid event)))
-      (org-show-entry))))
+      (org-fold-show-entry))))
 
 
 (defun gnus-icalendar--update-org-event (event reply-status &optional org-file)
diff --git a/lisp/language/european.el b/lisp/language/european.el
index 937215074bc..18b72bd3b2b 100644
--- a/lisp/language/european.el
+++ b/lisp/language/european.el
@@ -585,7 +585,7 @@ and it selects the Spanish tutorial."))
             (nonascii-translation . iso-8859-9)
             (unibyte-display . iso-latin-5)
             (input-method . "turkish-postfix")
-            (sample-text . "Turkish (Türkçe)   Merhaba")
+            (sample-text . "Turkish (Türkçe)   Esenlikler / Merhaba")
             (setup-function . turkish-case-conversion-enable)
             (exit-function . turkish-case-conversion-disable)
             (documentation . "Support for Turkish.
diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
index 7b41718a745..824325d83e0 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -234,14 +234,14 @@ MODE is either `c' or `cpp'."
      (false) @font-lock-constant-face
      (null) @font-lock-constant-face
      ,@(when (eq mode 'cpp)
-         '((this) @font-lock-constant-face
-           (nullptr) @font-lock-constant-face)))
+         '((nullptr) @font-lock-constant-face)))
 
    :language mode
    :feature 'keyword
    `([,@(c-ts-mode--keywords mode)] @font-lock-keyword-face
      ,@(when (eq mode 'cpp)
-         '((auto) @font-lock-keyword-face)))
+         '((auto) @font-lock-keyword-face
+           (this) @font-lock-keyword-face)))
 
    :language mode
    :feature 'operator
diff --git a/lisp/progmodes/csharp-mode.el b/lisp/progmodes/csharp-mode.el
index d0465b26f05..8a7313b1ce8 100644
--- a/lisp/progmodes/csharp-mode.el
+++ b/lisp/progmodes/csharp-mode.el
@@ -818,7 +818,13 @@ compilation and evaluation time conflicts."
 
    :language 'c-sharp
    :feature 'delimiter
-   '((["," ":" ";"]) @font-lock-delimiter-face)))
+   '((["," ":" ";"]) @font-lock-delimiter-face)
+
+   :language 'c-sharp
+   :feature 'escape-sequence
+   :override t
+   '((escape_sequence) @font-lock-escape-face
+     (ERROR) @font-lock-warning-face)))
 
 ;;;###autoload
 (add-to-list 'auto-mode-alist '("\\.cs\\'" . csharp-mode))
@@ -926,7 +932,7 @@ Key bindings:
   (setq-local treesit-font-lock-settings csharp-ts-mode--font-lock-settings)
   (setq-local treesit-font-lock-feature-list
               '(( comment definition)
-                ( keyword string type)
+                ( keyword string escape-sequence type)
                 ( attribute constant expression literal)
                 ( bracket delimiter)))
 
diff --git a/lisp/progmodes/dockerfile-ts-mode.el 
b/lisp/progmodes/dockerfile-ts-mode.el
new file mode 100644
index 00000000000..e08387ad969
--- /dev/null
+++ b/lisp/progmodes/dockerfile-ts-mode.el
@@ -0,0 +1,176 @@
+;;; dockerfile-ts-mode.el --- tree-sitter support for Dockerfiles  -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author     : Randy Taylor <dev@rjt.dev>
+;; Maintainer : Randy Taylor <dev@rjt.dev>
+;; Created    : December 2022
+;; Keywords   : dockerfile languages tree-sitter
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+
+;;; Code:
+
+(require 'treesit)
+(eval-when-compile (require 'rx))
+
+(declare-function treesit-parser-create "treesit.c")
+(declare-function treesit-induce-sparse-tree "treesit.c")
+(declare-function treesit-node-child "treesit.c")
+(declare-function treesit-node-start "treesit.c")
+(declare-function treesit-node-type "treesit.c")
+
+(defvar dockerfile-ts-mode--syntax-table
+  (let ((table (make-syntax-table)))
+    (modify-syntax-entry ?#  "<" table)
+    (modify-syntax-entry ?\n ">" table)
+    table)
+  "Syntax table for `dockerfile-ts-mode'.")
+
+(defvar dockerfile-ts-mode--indent-rules
+  `((dockerfile
+     ((parent-is "copy_instruction") (nth-sibling 1) 0)
+     ((parent-is "env_instruction") (nth-sibling 1) 0)
+     ((parent-is "expose_instruction") (nth-sibling 1) 0)
+     ((parent-is "label_instruction") (nth-sibling 1) 0)
+     ((parent-is "shell_command") first-sibling 0)
+     ((parent-is "string_array") first-sibling 1)))
+  "Tree-sitter indent rules.")
+
+(defvar dockerfile-ts-mode--keywords
+  '("ADD" "ARG" "AS" "CMD" "COPY" "CROSS_BUILD" "ENTRYPOINT" "ENV"
+    "EXPOSE" "FROM" "HEALTHCHECK" "LABEL" "MAINTAINER" "ONBUILD" "RUN"
+    "SHELL" "STOPSIGNAL" "USER" "VOLUME" "WORKDIR")
+  "Dockerfile keywords for tree-sitter font-locking.")
+
+(defvar dockerfile-ts-mode--font-lock-settings
+  (treesit-font-lock-rules
+   :language 'dockerfile
+   :feature 'bracket
+   '((["[" "]"]) @font-lock-bracket-face)
+
+   :language 'dockerfile
+   :feature 'comment
+   '((comment) @font-lock-comment-face)
+
+   :language 'dockerfile
+   :feature 'delimiter
+   '(([","]) @font-lock-delimiter-face)
+
+   :language 'dockerfile
+   :feature 'image-spec
+   '((image_spec) @font-lock-constant-face)
+
+   :language 'dockerfile
+   :feature 'keyword
+   `([,@dockerfile-ts-mode--keywords] @font-lock-keyword-face)
+
+   :language 'dockerfile
+   :feature 'number
+   '((expose_port) @font-lock-number-face)
+
+   :language 'dockerfile
+   :feature 'operator
+   '((["="]) @font-lock-operator-face)
+
+   :language 'dockerfile
+   :feature 'string
+   '((double_quoted_string) @font-lock-string-face)
+
+   :language 'dockerfile
+   :feature 'error
+   :override t
+   '((ERROR) @font-lock-warning-face))
+  "Tree-sitter font-lock settings.")
+
+(defun dockerfile-ts-mode--imenu ()
+  "Return Imenu alist for the current buffer."
+  (let* ((node (treesit-buffer-root-node))
+         (stage-tree (treesit-induce-sparse-tree
+                      node "from_instruction"
+                      nil 1000)))
+    `(("Stage" . ,(dockerfile-ts-mode--imenu-1 stage-tree)))))
+
+(defun dockerfile-ts-mode--imenu-1 (node)
+  "Helper for `dockerfile-ts-mode--imenu'.
+Find string representation for NODE and set marker, then recurse
+the subtrees."
+  (let* ((ts-node (car node))
+         (children (cdr node))
+         (subtrees (mapcan #'dockerfile-ts-mode--imenu-1
+                           children))
+         (name (when ts-node
+                 (pcase (treesit-node-type ts-node)
+                   ("from_instruction" (treesit-node-text
+                                        (treesit-node-child ts-node 1) t)))))
+         (marker (when ts-node
+                   (set-marker (make-marker)
+                               (treesit-node-start ts-node)))))
+    (cond
+     ((or (null ts-node) (null name)) subtrees)
+     (subtrees
+      `((,name ,(cons name marker) ,@subtrees)))
+     (t
+      `((,name . ,marker))))))
+
+;;;###autoload
+(add-to-list 'auto-mode-alist
+             `(,(rx (|
+                     (: "Dockerfile" (? "." (* nonl)))
+                     (: "." (any "dD") "ockerfile"))
+                    eol)
+               . dockerfile-ts-mode))
+
+;;;###autoload
+(define-derived-mode dockerfile-ts-mode prog-mode "Dockerfile"
+  "Major mode for editing Dockerfiles, powered by tree-sitter."
+  :group 'dockerfile
+  :syntax-table dockerfile-ts-mode--syntax-table
+
+  (when (treesit-ready-p 'dockerfile)
+    (treesit-parser-create 'dockerfile)
+
+    ;; Comments.
+    (setq-local comment-start "# ")
+    (setq-local comment-end "")
+    (setq-local comment-start-skip (rx "#" (* (syntax whitespace))))
+
+    ;; Imenu.
+    (setq-local imenu-create-index-function
+                #'dockerfile-ts-mode--imenu)
+    (setq-local which-func-functions nil)
+
+    ;; Indent.
+    (setq-local treesit-simple-indent-rules
+                dockerfile-ts-mode--indent-rules)
+
+    ;; Font-lock.
+    (setq-local treesit-font-lock-settings
+                dockerfile-ts-mode--font-lock-settings)
+    (setq-local treesit-font-lock-feature-list
+                '((comment)
+                  (keyword string)
+                  (image-spec number)
+                  (bracket delimiter error operator)))
+
+    (treesit-major-mode-setup)))
+
+(provide 'dockerfile-ts-mode)
+
+;;; dockerfile-ts-mode.el ends here
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index a53f62fc565..2ef022992e7 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -236,7 +236,7 @@ chosen (interactively or automatically)."
                                  . ,(eglot-alternatives 
'(("vscode-css-language-server" "--stdio")
                                                           
("css-languageserver" "--stdio"))))
                                 (html-mode . ,(eglot-alternatives 
'(("vscode-html-language-server" "--stdio") ("html-languageserver" "--stdio"))))
-                                (dockerfile-mode . ("docker-langserver" 
"--stdio"))
+                                ((dockerfile-mode dockerfile-ts-mode) . 
("docker-langserver" "--stdio"))
                                 ((clojure-mode clojurescript-mode 
clojurec-mode)
                                  . ("clojure-lsp"))
                                 ((csharp-mode csharp-ts-mode)
diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el
index 23e166ee4c3..9155a7fff25 100644
--- a/lisp/progmodes/java-ts-mode.el
+++ b/lisp/progmodes/java-ts-mode.el
@@ -321,7 +321,15 @@ the subtrees."
               (append "{}():;," electric-indent-chars))
 
   ;; Navigation.
-  (setq-local treesit-defun-type-regexp "declaration")
+  (setq-local treesit-defun-type-regexp
+              (regexp-opt '("method_declaration"
+                            "class_declaration"
+                            "record_declaration"
+                            "interface_declaration"
+                            "enum_declaration"
+                            "import_declaration"
+                            "package_declaration"
+                            "module_declaration")))
 
   ;; Font-lock.
   (setq-local treesit-font-lock-settings java-ts-mode--font-lock-settings)
diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el
index 45dfef372cd..f7318c481a2 100644
--- a/lisp/progmodes/js.el
+++ b/lisp/progmodes/js.el
@@ -3543,6 +3543,20 @@ This function is intended for use in 
`after-change-functions'."
      (arrow_function
       parameter: (identifier) @font-lock-variable-name-face))
 
+   :language 'javascript
+   :override t
+   :feature 'property
+   ;; This needs to be before function-name feature, because methods
+   ;; can be both property and function-name, and we want them in
+   ;; function-name face.
+   `((property_identifier) @font-lock-property-face
+
+     (pair value: (identifier) @font-lock-variable-name-face)
+
+     ((shorthand_property_identifier) @font-lock-property-face)
+
+     ((shorthand_property_identifier_pattern) @font-lock-property-face))
+
    :language 'javascript
    :override t
    :feature 'expression
@@ -3611,18 +3625,7 @@ This function is intended for use in 
`after-change-functions'."
    :language 'javascript
    :feature 'escape-sequence
    :override t
-   '((escape_sequence) @font-lock-escape-face)
-
-   :language 'javascript
-   :override t
-   :feature 'property
-   `((property_identifier) @font-lock-property-face
-
-     (pair value: (identifier) @font-lock-variable-name-face)
-
-     ((shorthand_property_identifier) @font-lock-property-face)
-
-     ((shorthand_property_identifier_pattern) @font-lock-property-face)))
+   '((escape_sequence) @font-lock-escape-face))
   "Tree-sitter font-lock settings.")
 
 (defun js--fontify-template-string (node override start end &rest _)
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index 38d4fdad5fc..016dfdd5b4d 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -515,7 +515,8 @@ project backend implementation of 
`project-external-roots'.")
              (marker-re
               (mapconcat
                (lambda (m) (format "\\(%s\\)" (wildcard-to-regexp m)))
-               (append backend-markers project-vc-extra-root-markers)
+               (append backend-markers
+                       (project--value-in-dir 'project-vc-extra-root-markers 
dir))
                "\\|"))
              (locate-dominating-stop-dir-regexp
               (or vc-ignore-dir-regexp locate-dominating-stop-dir-regexp))
@@ -535,7 +536,7 @@ project backend implementation of 
`project-external-roots'.")
              project)
         (when (and
                (eq backend 'Git)
-               project-vc-merge-submodules
+               (project--vc-merge-submodules-p root)
                (project--submodule-p root))
           (let* ((parent (file-name-directory (directory-file-name root))))
             (setq root (vc-call-backend 'Git 'root parent))))
@@ -582,7 +583,7 @@ project backend implementation of 
`project-external-roots'.")
 (cl-defmethod project-files ((project (head vc)) &optional dirs)
   (mapcan
    (lambda (dir)
-     (let ((ignores project-vc-ignores)
+     (let ((ignores (project--value-in-dir 'project-vc-ignores (nth 2 
project)))
            (backend (cadr project)))
        (when backend
          (require (intern (concat "vc-" (downcase (symbol-name backend))))))
@@ -608,13 +609,16 @@ project backend implementation of 
`project-external-roots'.")
   (defvar vc-git-use-literal-pathspecs)
   (pcase backend
     (`Git
-     (let ((default-directory (expand-file-name (file-name-as-directory dir)))
-           (args '("-z"))
-           (vc-git-use-literal-pathspecs nil)
-           files)
+     (let* ((default-directory (expand-file-name (file-name-as-directory dir)))
+            (args '("-z"))
+            (vc-git-use-literal-pathspecs nil)
+            (include-untracked (project--value-in-dir
+                                'project-vc-include-untracked
+                                dir))
+            files)
        (setq args (append args
                           '("-c" "--exclude-standard")
-                          (and project-vc-include-untracked '("-o"))))
+                          (and include-untracked '("-o"))))
        (when extra-ignores
          (setq args (append args
                             (cons "--"
@@ -647,7 +651,7 @@ project backend implementation of 
`project-external-roots'.")
               (split-string
                (apply #'vc-git--run-command-string nil "ls-files" args)
                "\0" t)))
-       (when project-vc-merge-submodules
+       (when (project--vc-merge-submodules-p default-directory)
          ;; Unfortunately, 'ls-files --recurse-submodules' conflicts with '-o'.
          (let* ((submodules (project--git-submodules))
                 (sub-files
@@ -665,10 +669,13 @@ project backend implementation of 
`project-external-roots'.")
        ;; XXX: Better solutions welcome, but this seems cheap enough.
        (delete-consecutive-dups files)))
     (`Hg
-     (let ((default-directory (expand-file-name (file-name-as-directory dir)))
-           (args (list (concat "-mcard" (and project-vc-include-untracked "u"))
-                       "--no-status"
-                       "-0")))
+     (let* ((default-directory (expand-file-name (file-name-as-directory dir)))
+            (include-untracked (project--value-in-dir
+                                'project-vc-include-untracked
+                                dir))
+            (args (list (concat "-mcard" (and include-untracked "u"))
+                        "--no-status"
+                        "-0")))
        (when extra-ignores
          (setq args (nconc args
                            (mapcan
@@ -681,6 +688,11 @@ project backend implementation of 
`project-external-roots'.")
           (lambda (s) (concat default-directory s))
           (split-string (buffer-string) "\0" t)))))))
 
+(defun project--vc-merge-submodules-p (dir)
+  (project--value-in-dir
+   'project-vc-merge-submodules
+   dir))
+
 (defun project--git-submodules ()
   ;; 'git submodule foreach' is much slower.
   (condition-case nil
@@ -722,7 +734,7 @@ project backend implementation of 
`project-external-roots'.")
          (condition-case nil
              (vc-call-backend backend 'ignore-completion-table root)
            (vc-not-supported () nil)))))
-     project-vc-ignores
+     (project--value-in-dir 'project-vc-ignores root)
      (mapcar
       (lambda (dir)
         (concat dir "/"))
@@ -753,9 +765,16 @@ DIRS must contain directory names."
   ;; Sidestep the issue of expanded/abbreviated file names here.
   (cl-set-difference files dirs :test #'file-in-directory-p))
 
+(defun project--value-in-dir (var dir)
+  (with-temp-buffer
+    (setq default-directory dir)
+    (let ((enable-local-variables :all))
+      (hack-dir-local-variables-non-file-buffer))
+    (symbol-value var)))
+
 (cl-defmethod project-buffers ((project (head vc)))
   (let* ((root (expand-file-name (file-name-as-directory (project-root 
project))))
-         (modules (unless (or project-vc-merge-submodules
+         (modules (unless (or (project--vc-merge-submodules-p root)
                               (project--submodule-p root))
                     (mapcar
                      (lambda (m) (format "%s%s/" root m))
diff --git a/lisp/progmodes/typescript-ts-mode.el 
b/lisp/progmodes/typescript-ts-mode.el
index 243f6146ae7..8c4364ecc5b 100644
--- a/lisp/progmodes/typescript-ts-mode.el
+++ b/lisp/progmodes/typescript-ts-mode.el
@@ -89,6 +89,7 @@ Argument LANGUAGE is either `typescript' or `tsx'."
      ((parent-is "object") parent-bol typescript-ts-mode-indent-offset)
      ((parent-is "object_type") parent-bol typescript-ts-mode-indent-offset)
      ((parent-is "enum_body") parent-bol typescript-ts-mode-indent-offset)
+     ((parent-is "class_body") parent-bol typescript-ts-mode-indent-offset)
      ((parent-is "arrow_function") parent-bol typescript-ts-mode-indent-offset)
      ((parent-is "parenthesized_expression") parent-bol 
typescript-ts-mode-indent-offset)
 
@@ -361,8 +362,7 @@ Argument LANGUAGE is either `typescript' or `tsx'."
     (setq-local treesit-font-lock-settings
                 (typescript-ts-mode--font-lock-settings 'typescript))
     (setq-local treesit-font-lock-feature-list
-                '((comment declaration)
-                  (keyword string)
+                '((comment declaration keyword string escape-sequence)
                   (constant expression identifier number pattern property)
                   (bracket delimiter)))
 
@@ -396,8 +396,7 @@ Argument LANGUAGE is either `typescript' or `tsx'."
     (setq-local treesit-font-lock-settings
                 (typescript-ts-mode--font-lock-settings 'tsx))
     (setq-local treesit-font-lock-feature-list
-                '((comment declaration)
-                  (keyword string)
+                '((comment declaration keyword string escape-sequence)
                   (constant expression identifier jsx number pattern property)
                   (bracket delimiter)))
 
diff --git a/src/treesit.c b/src/treesit.c
index 8b485ca4ece..d361a3da932 100644
--- a/src/treesit.c
+++ b/src/treesit.c
@@ -384,7 +384,18 @@ init_treesit_functions (void)
    mysteriously drops.  3) what if a user uses so many stuff that the
    default cache size (20) is not enough and we end up thrashing?
    These are all imaginary scenarios but they are not impossible
-   :-) */
+   :-)
+
+   Parsers in indirect buffers: We make indirect buffers to share the
+   parser of its base buffer.  Indirect buffers and their base buffer
+   share the same buffer content but not other buffer attributes.  If
+   they have separate parser lists, changes made in an indirect buffer
+   will only update parsers of that indirect buffer, and not parsers
+   in the base buffer or other indirect buffers, and vice versa.  We
+   could keep track of all the base and indirect buffers, and update
+   all of their parsers, but ultimately decide to take a simpler
+   approach, which is to make indirect buffers share their base
+   buffer's parser list.  The discussion can be found in bug#59693.  */
 
 
 /*** Initialization */
@@ -697,9 +708,10 @@ void
 treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte,
                       ptrdiff_t new_end_byte)
 {
-  Lisp_Object parser_list;
-
-  parser_list = BVAR (current_buffer, ts_parser_list);
+  struct buffer *base_buffer = current_buffer;
+  if (current_buffer->base_buffer)
+    base_buffer = current_buffer->base_buffer;
+  Lisp_Object parser_list = BVAR (base_buffer, ts_parser_list);
 
   FOR_EACH_TAIL_SAFE (parser_list)
     {
@@ -1252,12 +1264,16 @@ DEFUN ("treesit-parser-create",
        1, 3, 0,
        doc: /* Create and return a parser in BUFFER for LANGUAGE.
 
-The parser is automatically added to BUFFER's parser list, as
-returned by `treesit-parser-list'.
-LANGUAGE is a language symbol.  If BUFFER is nil or omitted, it
-defaults to the current buffer.  If BUFFER already has a parser for
-LANGUAGE, return that parser, but if NO-REUSE is non-nil, always
-create a new parser.  */)
+The parser is automatically added to BUFFER's parser list, as returned
+by `treesit-parser-list'.  LANGUAGE is a language symbol.  If BUFFER
+is nil or omitted, it defaults to the current buffer.  If BUFFER
+already has a parser for LANGUAGE, return that parser, but if NO-REUSE
+is non-nil, always create a new parser.
+
+If that buffer is an indirect buffer, its base buffer is used instead.
+That is, indirect buffers use their base buffer's parsers.  Lisp
+programs should widen as necessary should they want to use a parser in
+an indirect buffer.  */)
   (Lisp_Object language, Lisp_Object buffer, Lisp_Object no_reuse)
 {
   treesit_initialize ();
@@ -1271,16 +1287,21 @@ create a new parser.  */)
       CHECK_BUFFER (buffer);
       buf = XBUFFER (buffer);
     }
+  if (buf->base_buffer)
+    buf = buf->base_buffer;
+
   treesit_check_buffer_size (buf);
 
   /* See if we can reuse a parser.  */
-  for (Lisp_Object tail = BVAR (buf, ts_parser_list);
-       NILP (no_reuse) && !NILP (tail);
-       tail = XCDR (tail))
+  if (NILP (no_reuse))
     {
-      struct Lisp_TS_Parser *parser = XTS_PARSER (XCAR (tail));
-      if (EQ (parser->language_symbol, language))
-       return XCAR (tail);
+      Lisp_Object tail = BVAR (buf, ts_parser_list);
+      FOR_EACH_TAIL (tail)
+      {
+       struct Lisp_TS_Parser *parser = XTS_PARSER (XCAR (tail));
+       if (EQ (parser->language_symbol, language))
+         return XCAR (tail);
+      }
     }
 
   /* Load language.  */
@@ -1329,7 +1350,10 @@ DEFUN ("treesit-parser-list",
        Ftreesit_parser_list, Streesit_parser_list,
        0, 1, 0,
        doc: /* Return BUFFER's parser list.
-BUFFER defaults to the current buffer.  */)
+
+BUFFER defaults to the current buffer.  If that buffer is an indirect
+buffer, its base buffer is used instead.  That is, indirect buffers
+use their base buffer's parsers.  */)
   (Lisp_Object buffer)
 {
   struct buffer *buf;
@@ -1340,6 +1364,9 @@ BUFFER defaults to the current buffer.  */)
       CHECK_BUFFER (buffer);
       buf = XBUFFER (buffer);
     }
+  if (buf->base_buffer)
+    buf = buf->base_buffer;
+
   /* Return a fresh list so messing with that list doesn't affect our
      internal data.  */
   Lisp_Object return_list = Qnil;
diff --git a/test/lisp/progmodes/project-resources/.dir-locals.el 
b/test/lisp/progmodes/project-resources/.dir-locals.el
new file mode 100644
index 00000000000..a311b7efa9a
--- /dev/null
+++ b/test/lisp/progmodes/project-resources/.dir-locals.el
@@ -0,0 +1 @@
+((nil . ((project-vc-ignores . ("etc")))))
diff --git a/test/lisp/progmodes/project-resources/etc 
b/test/lisp/progmodes/project-resources/etc
new file mode 100644
index 00000000000..dd7999bd3dd
--- /dev/null
+++ b/test/lisp/progmodes/project-resources/etc
@@ -0,0 +1 @@
+etc
\ No newline at end of file
diff --git a/test/lisp/progmodes/project-resources/foo 
b/test/lisp/progmodes/project-resources/foo
new file mode 100644
index 00000000000..19102815663
--- /dev/null
+++ b/test/lisp/progmodes/project-resources/foo
@@ -0,0 +1 @@
+foo
\ No newline at end of file
diff --git a/test/lisp/progmodes/project-tests.el 
b/test/lisp/progmodes/project-tests.el
index c3b886873d3..8814f30b047 100644
--- a/test/lisp/progmodes/project-tests.el
+++ b/test/lisp/progmodes/project-tests.el
@@ -41,7 +41,7 @@ quoted directory names (Bug#47799)."
   (skip-unless (executable-find "grep"))
   (ert-with-temp-directory directory
     (let ((default-directory directory)
-          (project-current-inhibit-prompt t)
+          (project-current-directory-override t)
           (project-find-functions nil)
           (project-list-file
            (expand-file-name "projects" directory))
@@ -139,4 +139,17 @@ When `project-ignores' includes a name matching project 
dir."
     (should-not (null project))
     (should (string-match-p "/test/lisp/\\'" (project-root project)))))
 
+(ert-deftest project-vc-supports-project-in-different-dir ()
+  "Check that it picks up dir-locals settings from somewhere else."
+  (skip-unless (eq (vc-responsible-backend default-directory) 'Git))
+  (let* ((dir (ert-resource-directory))
+         (_ (vc-file-clearprops dir))
+         (project-vc-extra-root-markers '(".dir-locals.el"))
+         (project (project-current nil dir)))
+    (should-not (null project))
+    (should (string-match-p "/test/lisp/progmodes/project-resources/\\'" 
(project-root project)))
+    (should (member "etc" (project-ignores project dir)))
+    (should (equal '(".dir-locals.el" "foo")
+                   (mapcar #'file-name-nondirectory (project-files 
project))))))
+
 ;;; project-tests.el ends here
diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el
index aba12759c34..1cc2217bd3b 100644
--- a/test/src/treesit-tests.el
+++ b/test/src/treesit-tests.el
@@ -161,6 +161,40 @@
       (should (treesit-node-eq root-node root-node))
       (should (not (treesit-node-eq root-node doc-node))))))
 
+(ert-deftest treesit-indirect-buffer ()
+  "Tests for indirect buffers."
+  (skip-unless (treesit-language-available-p 'json))
+  (let ((base (get-buffer-create "*treesit test*"))
+        parser indirect)
+    (unwind-protect
+        (progn
+          (with-current-buffer base
+            (setq indirect (clone-indirect-buffer "*treesit test 1*" nil)))
+          (with-current-buffer indirect
+            (setq parser (treesit-parser-create 'json)))
+          ;; 1. Parser created in the indirect buffer should be
+          ;; actually be created in the base buffer.
+          (with-current-buffer base
+            (should (equal (list parser)
+                           (treesit-parser-list)))
+            (insert "[1,2,3]"))
+          ;; Change in the base buffer should be reflected in the
+          ;; indirect buffer.
+          (with-current-buffer indirect
+            (should (eq (treesit-node-end
+                         (treesit-buffer-root-node))
+                        8))
+            (erase-buffer))
+          ;; Change in the indirect buffer should be reflected in the
+          ;; base buffer.
+          (with-current-buffer base
+            (should (eq (treesit-node-end
+                         (treesit-buffer-root-node))
+                        1))
+            (erase-buffer)))
+      (kill-buffer base)
+      (kill-buffer indirect))))
+
 (ert-deftest treesit-query-api ()
   "Tests for query API."
   (skip-unless (treesit-language-available-p 'json))



reply via email to

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