aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPo Lu2022-12-11 09:34:03 +0800
committerPo Lu2022-12-11 09:34:03 +0800
commit6d6ca47aba7b72d2a770d7ed01c849d3cc729e21 (patch)
treee8181f7188a3a74147549b7ba76ea9c3a696259c
parente08564432918aa87b49da58ac90bb43718424364 (diff)
parent44c5f3614973d8dc389ddcdc1b3f8ab1c809194d (diff)
downloademacs-6d6ca47aba7b72d2a770d7ed01c849d3cc729e21.tar.gz
emacs-6d6ca47aba7b72d2a770d7ed01c849d3cc729e21.zip
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
-rwxr-xr-xadmin/notes/tree-sitter/build-module/batch.sh1
-rwxr-xr-xadmin/notes/tree-sitter/build-module/build.sh6
-rw-r--r--admin/notes/tree-sitter/starter-guide2
-rw-r--r--doc/lispref/parsing.texi10
-rw-r--r--doc/misc/use-package.texi4
-rw-r--r--etc/HELLO2
-rw-r--r--etc/NEWS5
-rw-r--r--lib-src/seccomp-filter.c2
-rw-r--r--lisp/emacs-lisp/package-vc.el56
-rw-r--r--lisp/gnus/gnus-icalendar.el2
-rw-r--r--lisp/language/european.el2
-rw-r--r--lisp/progmodes/c-ts-mode.el6
-rw-r--r--lisp/progmodes/csharp-mode.el10
-rw-r--r--lisp/progmodes/dockerfile-ts-mode.el176
-rw-r--r--lisp/progmodes/eglot.el2
-rw-r--r--lisp/progmodes/java-ts-mode.el10
-rw-r--r--lisp/progmodes/js.el27
-rw-r--r--lisp/progmodes/project.el49
-rw-r--r--lisp/progmodes/typescript-ts-mode.el7
-rw-r--r--src/treesit.c61
-rw-r--r--test/lisp/progmodes/project-resources/.dir-locals.el1
-rw-r--r--test/lisp/progmodes/project-resources/etc1
-rw-r--r--test/lisp/progmodes/project-resources/foo1
-rw-r--r--test/lisp/progmodes/project-tests.el15
-rw-r--r--test/src/treesit-tests.el34
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=(
5 'cpp' 5 'cpp'
6 'css' 6 'css'
7 'c-sharp' 7 'c-sharp'
8 'dockerfile'
8 'go' 9 'go'
9 'html' 10 'html'
10 'javascript' 11 '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}"
14 14
15### Retrieve sources 15### Retrieve sources
16 16
17namespace="tree-sitter"
17repo="tree-sitter-${lang}" 18repo="tree-sitter-${lang}"
18sourcedir="tree-sitter-${lang}/src" 19sourcedir="tree-sitter-${lang}/src"
19grammardir="tree-sitter-${lang}" 20grammardir="tree-sitter-${lang}"
20 21
21case "${lang}" in 22case "${lang}" in
23 "dockerfile")
24 namespace="camdencheek"
25 ;;
22 "typescript") 26 "typescript")
23 sourcedir="tree-sitter-typescript/typescript/src" 27 sourcedir="tree-sitter-typescript/typescript/src"
24 grammardir="tree-sitter-typescript/typescript" 28 grammardir="tree-sitter-typescript/typescript"
@@ -30,7 +34,7 @@ case "${lang}" in
30 ;; 34 ;;
31esac 35esac
32 36
33git clone "https://github.com/tree-sitter/${repo}.git" \ 37git clone "https://github.com/${namespace}/${repo}.git" \
34 --depth 1 --quiet 38 --depth 1 --quiet
35cp "${grammardir}"/grammar.js "${sourcedir}" 39cp "${grammardir}"/grammar.js "${sourcedir}"
36# We have to go into the source directory to compile, because some 40# 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
282 NODE-INDEX-MIN NODE-INDEX-MAX) 282 NODE-INDEX-MIN NODE-INDEX-MAX)
283 283
284 => checks everything. If an argument is nil, don’t match that. Eg, 284 => checks everything. If an argument is nil, don’t match that. Eg,
285 (match nil nil TYPE) is the same as (parent-is TYPE) 285 (match nil TYPE) is the same as (parent-is TYPE)
286 286
287For ANCHOR we have 287For ANCHOR we have
288 288
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}
409By default, this function reuses a parser if one already exists for 409By default, this function reuses a parser if one already exists for
410@var{language} in @var{buffer}, but if @var{no-reuse} is 410@var{language} in @var{buffer}, but if @var{no-reuse} is
411non-@code{nil}, this function always creates a new parser. 411non-@code{nil}, this function always creates a new parser.
412
413If that buffer is an indirect buffer, its base buffer is used instead.
414That is, indirect buffers use their base buffer's parsers. If the
415base buffer is narrowed, an indirect buffer might not be able to
416retrieve information of the portion of the buffer text that are
417invisible in the base buffer. Lisp programs should widen as necessary
418should they want to use a parser in an indirect buffer.
412@end defun 419@end defun
413 420
414Given a parser, we can query information about it. 421Given a parser, we can query information about it.
@@ -447,7 +454,8 @@ tree incrementally.
447@defun treesit-parser-list &optional buffer 454@defun treesit-parser-list &optional buffer
448This function returns the parser list of @var{buffer}. If 455This function returns the parser list of @var{buffer}. If
449@var{buffer} is @code{nil} or omitted, it defaults to the current 456@var{buffer} is @code{nil} or omitted, it defaults to the current
450buffer. 457buffer. If that buffer is an indirect buffer, its base buffer is used
458instead. That is, indirect buffers use their base buffer's parsers.
451@end defun 459@end defun
452 460
453@defun treesit-parser-delete parser 461@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
190that will slow down Emacs startup. Instead, you should try to set 190that will slow down Emacs startup. Instead, you should try to set
191things up so that packages are only loaded when they are actually 191things up so that packages are only loaded when they are actually
192needed (a.k.a. ``autoloading''). If you have installed a package from 192needed (a.k.a. ``autoloading''). If you have installed a package from
193@acronym{GNU ELPA} that provides it's own autoloads, it is often 193@acronym{GNU} @acronym{ELPA} that provides it's own autoloads, it is often
194enough to say: 194enough to say:
195 195
196@lisp 196@lisp
@@ -1273,7 +1273,7 @@ cells, or a string or regexp.
1273The following example reproduces the default @code{ruby-mode} 1273The following example reproduces the default @code{ruby-mode}
1274configuration, exactly as it is in Emacs out-of-the-box. That mode is 1274configuration, exactly as it is in Emacs out-of-the-box. That mode is
1275enabled automatically when a file whose name matches the regexp 1275enabled automatically when a file whose name matches the regexp
1276@code{"\\.rb\\'"} (a file with the @samp{.rb} extension), or when the 1276@code{"\\.rb\\'"} (a file with the @file{.rb} extension), or when the
1277first line of the file (known as the ``shebang'') matches the string 1277first line of the file (known as the ``shebang'') matches the string
1278@code{"ruby"}: 1278@code{"ruby"}:
1279 1279
diff --git a/etc/HELLO b/etc/HELLO
index 7bc12063f8e..fbe65a451e1 100644
--- a/etc/HELLO
+++ b/etc/HELLO
@@ -114,7 +114,7 @@ Thai (ภาษาไทย) สวัสดีครับ / สวัสดี
114Tibetan (བོད་སྐད་) བཀྲ་ཤིས་བདེ་ལེགས༎ 114Tibetan (བོད་སྐད་) བཀྲ་ཤིས་བདེ་ལེགས༎
115Tigrigna (ትግርኛ) ሰላማት 115Tigrigna (ትግርኛ) ሰላማት
116Tirhuta (𑒞𑒱𑒩𑒯𑒳𑒞𑒰) 𑒣𑓂𑒩𑒢𑒰𑒧 / 𑒮𑒲𑒞𑒰𑒩𑒰𑒧 116Tirhuta (𑒞𑒱𑒩𑒯𑒳𑒞𑒰) 𑒣𑓂𑒩𑒢𑒰𑒧 / 𑒮𑒲𑒞𑒰𑒩𑒰𑒧
117Turkish (Türkçe) Merhaba 117Turkish (Türkçe) Esenlikler / Merhaba
118Ukrainian (українська) Вітаю / Добрий день! / Привіт 118Ukrainian (українська) Вітаю / Добрий день! / Привіт
119Vietnamese (tiếng Việt) Chào bạn 119Vietnamese (tiếng Việt) Chào bạn
120Wancho (𞋒𞋀𞋉𞋃𞋕) 𞋂𞋈𞋛 120Wancho (𞋒𞋀𞋉𞋃𞋕) 𞋂𞋈𞋛
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
79 79
80* New Modes and Packages in Emacs 30.1 80* New Modes and Packages in Emacs 30.1
81 81
82** New major mode 'dockerfile-ts-mode'.
83A major mode based on the tree-sitter library for editing
84Dockerfiles. It includes support for font-locking, indentation, Imenu,
85and which-func.
86
82 87
83* Incompatible Lisp Changes in Emacs 30.1 88* Incompatible Lisp Changes in Emacs 30.1
84 89
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)
342 RULE (SCMP_ACT_ALLOW, SCMP_SYS (eventfd2)); 342 RULE (SCMP_ACT_ALLOW, SCMP_SYS (eventfd2));
343 RULE (SCMP_ACT_ALLOW, SCMP_SYS (wait4)); 343 RULE (SCMP_ACT_ALLOW, SCMP_SYS (wait4));
344 RULE (SCMP_ACT_ALLOW, SCMP_SYS (poll)); 344 RULE (SCMP_ACT_ALLOW, SCMP_SYS (poll));
345 RULE (SCMP_ACT_ALLOW, SCMP_SYS (pidfd_open),
346 SCMP_A1_32 (SCMP_CMP_EQ, 0));
345 347
346 /* Don't allow creating sockets (network access would be extremely 348 /* Don't allow creating sockets (network access would be extremely
347 dangerous), but also don't crash. */ 349 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 @@
50(eval-when-compile (require 'rx)) 50(eval-when-compile (require 'rx))
51(eval-when-compile (require 'inline)) 51(eval-when-compile (require 'inline))
52(eval-when-compile (require 'map)) 52(eval-when-compile (require 'map))
53(eval-when-compile (require 'cl-lib))
53(require 'package) 54(require 'package)
54(require 'lisp-mnt) 55(require 'lisp-mnt)
55(require 'vc) 56(require 'vc)
@@ -293,21 +294,41 @@ asynchronously."
293 (insert-file-contents main-file) 294 (insert-file-contents main-file)
294 (package-strip-rcs-id 295 (package-strip-rcs-id
295 (or (lm-header "package-version") 296 (or (lm-header "package-version")
296 (lm-header "version")))) 297 (lm-header "version")
298 "0")))
297 "0")) 299 "0"))
298 300
299(defun package-vc--main-file (pkg-desc) 301(defun package-vc--main-file (pkg-desc)
300 "Return the name of the main file for PKG-DESC." 302 "Return the name of the main file for PKG-DESC."
301 (cl-assert (package-vc-p pkg-desc)) 303 (cl-assert (package-vc-p pkg-desc))
302 (let ((pkg-spec (package-vc--desc->spec pkg-desc)) 304 (let* ((pkg-spec (package-vc--desc->spec pkg-desc))
303 (name (symbol-name (package-desc-name pkg-desc)))) 305 (name (symbol-name (package-desc-name pkg-desc)))
304 (or (plist-get pkg-spec :main-file) 306 (directory (file-name-concat
305 (expand-file-name 307 (or (package-desc-dir pkg-desc)
306 (concat name ".el") 308 (expand-file-name name package-user-dir))
307 (file-name-concat 309 (plist-get pkg-spec :lisp-dir)))
308 (or (package-desc-dir pkg-desc) 310 (file (or (plist-get pkg-spec :main-file)
309 (expand-file-name name package-user-dir)) 311 (expand-file-name
310 (plist-get pkg-spec :lisp-dir)))))) 312 (concat name ".el")
313 directory))))
314 (if (file-exists-p file) file
315 ;; The following heuristic is only necessary when fetching a
316 ;; repository with URL that would break the above assumptions.
317 ;; Concrete example: https://github.com/sachac/waveform-el does
318 ;; not have a file waveform-el.el, but a file waveform.el, so we
319 ;; try and find the closest match.
320 (let ((distance most-positive-fixnum) (best nil))
321 (dolist (alt (directory-files directory t "\\.el\\'" t))
322 (let ((sd (string-distance file alt)))
323 (when (and (not (string-match-p (rx (or (: "-autoloads.el")
324 (: "-pkg.el"))
325 eos)
326 alt))
327 (< sd distance))
328 (when (< sd distance)
329 (setq distance (string-distance file alt)
330 best alt)))))
331 best))))
311 332
312(defun package-vc--generate-description-file (pkg-desc pkg-file) 333(defun package-vc--generate-description-file (pkg-desc pkg-file)
313 "Generate a package description file for PKG-DESC and write it to PKG-FILE." 334 "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."
466 (package--save-selected-packages 487 (package--save-selected-packages
467 (cons (package-desc-name pkg-desc) 488 (cons (package-desc-name pkg-desc)
468 package-selected-packages)) 489 package-selected-packages))
490 (package--quickstart-maybe-refresh)
469 491
470 ;; Confirm that the installation was successful 492 ;; Confirm that the installation was successful
471 (let ((main-file (package-vc--main-file pkg-desc))) 493 (let ((main-file (package-vc--main-file pkg-desc)))
@@ -710,11 +732,11 @@ regular package, but it will not remove a VC package."
710(defun package-vc-checkout (pkg-desc directory &optional rev) 732(defun package-vc-checkout (pkg-desc directory &optional rev)
711 "Clone the sources for PKG-DESC into DIRECTORY and visit that directory. 733 "Clone the sources for PKG-DESC into DIRECTORY and visit that directory.
712Unlike `package-vc-install', this does not yet set up the package 734Unlike `package-vc-install', this does not yet set up the package
713for use with Emacs; use `package-vc-link-directory' for setting 735for use with Emacs; use `package-vc-install-from-checkout' for
714the package up after this function finishes. 736setting the package up after this function finishes. Optional
715Optional argument REV means to clone a specific version of the 737argument REV means to clone a specific version of the package; it
716package; it defaults to the last version available from the 738defaults to the last version available from the package's
717package's repository. If REV has the special value 739repository. If REV has the special value
718`:last-release' (interactively, the prefix argument), that stands 740`:last-release' (interactively, the prefix argument), that stands
719for the last released version of the package." 741for the last released version of the package."
720 (interactive 742 (interactive
@@ -753,6 +775,10 @@ name from the base name of DIR."
753 (package-vc--archives-initialize) 775 (package-vc--archives-initialize)
754 (let* ((name (or name (file-name-base (directory-file-name dir)))) 776 (let* ((name (or name (file-name-base (directory-file-name dir))))
755 (pkg-dir (expand-file-name name package-user-dir))) 777 (pkg-dir (expand-file-name name package-user-dir)))
778 (when (file-exists-p pkg-dir)
779 (if (yes-or-no-p (format "Overwrite previous checkout for package `%s'?" name))
780 (package--delete-directory pkg-dir)
781 (error "There already exists a checkout for %s" name)))
756 (make-symbolic-link (expand-file-name dir) pkg-dir) 782 (make-symbolic-link (expand-file-name dir) pkg-dir)
757 (package-vc--unpack-1 783 (package-vc--unpack-1
758 (package-desc-create 784 (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."
603 (when file 603 (when file
604 (switch-to-buffer (find-file file)) 604 (switch-to-buffer (find-file file))
605 (goto-char (org-find-entry-with-id (gnus-icalendar-event:uid event))) 605 (goto-char (org-find-entry-with-id (gnus-icalendar-event:uid event)))
606 (org-show-entry)))) 606 (org-fold-show-entry))))
607 607
608 608
609(defun gnus-icalendar--update-org-event (event reply-status &optional org-file) 609(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."))
585 (nonascii-translation . iso-8859-9) 585 (nonascii-translation . iso-8859-9)
586 (unibyte-display . iso-latin-5) 586 (unibyte-display . iso-latin-5)
587 (input-method . "turkish-postfix") 587 (input-method . "turkish-postfix")
588 (sample-text . "Turkish (Türkçe) Merhaba") 588 (sample-text . "Turkish (Türkçe) Esenlikler / Merhaba")
589 (setup-function . turkish-case-conversion-enable) 589 (setup-function . turkish-case-conversion-enable)
590 (exit-function . turkish-case-conversion-disable) 590 (exit-function . turkish-case-conversion-disable)
591 (documentation . "Support for Turkish. 591 (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'."
234 (false) @font-lock-constant-face 234 (false) @font-lock-constant-face
235 (null) @font-lock-constant-face 235 (null) @font-lock-constant-face
236 ,@(when (eq mode 'cpp) 236 ,@(when (eq mode 'cpp)
237 '((this) @font-lock-constant-face 237 '((nullptr) @font-lock-constant-face)))
238 (nullptr) @font-lock-constant-face)))
239 238
240 :language mode 239 :language mode
241 :feature 'keyword 240 :feature 'keyword
242 `([,@(c-ts-mode--keywords mode)] @font-lock-keyword-face 241 `([,@(c-ts-mode--keywords mode)] @font-lock-keyword-face
243 ,@(when (eq mode 'cpp) 242 ,@(when (eq mode 'cpp)
244 '((auto) @font-lock-keyword-face))) 243 '((auto) @font-lock-keyword-face
244 (this) @font-lock-keyword-face)))
245 245
246 :language mode 246 :language mode
247 :feature 'operator 247 :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."
818 818
819 :language 'c-sharp 819 :language 'c-sharp
820 :feature 'delimiter 820 :feature 'delimiter
821 '((["," ":" ";"]) @font-lock-delimiter-face))) 821 '((["," ":" ";"]) @font-lock-delimiter-face)
822
823 :language 'c-sharp
824 :feature 'escape-sequence
825 :override t
826 '((escape_sequence) @font-lock-escape-face
827 (ERROR) @font-lock-warning-face)))
822 828
823;;;###autoload 829;;;###autoload
824(add-to-list 'auto-mode-alist '("\\.cs\\'" . csharp-mode)) 830(add-to-list 'auto-mode-alist '("\\.cs\\'" . csharp-mode))
@@ -926,7 +932,7 @@ Key bindings:
926 (setq-local treesit-font-lock-settings csharp-ts-mode--font-lock-settings) 932 (setq-local treesit-font-lock-settings csharp-ts-mode--font-lock-settings)
927 (setq-local treesit-font-lock-feature-list 933 (setq-local treesit-font-lock-feature-list
928 '(( comment definition) 934 '(( comment definition)
929 ( keyword string type) 935 ( keyword string escape-sequence type)
930 ( attribute constant expression literal) 936 ( attribute constant expression literal)
931 ( bracket delimiter))) 937 ( bracket delimiter)))
932 938
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 @@
1;;; dockerfile-ts-mode.el --- tree-sitter support for Dockerfiles -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2022 Free Software Foundation, Inc.
4
5;; Author : Randy Taylor <dev@rjt.dev>
6;; Maintainer : Randy Taylor <dev@rjt.dev>
7;; Created : December 2022
8;; Keywords : dockerfile languages tree-sitter
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software: you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
24
25;;; Commentary:
26;;
27
28;;; Code:
29
30(require 'treesit)
31(eval-when-compile (require 'rx))
32
33(declare-function treesit-parser-create "treesit.c")
34(declare-function treesit-induce-sparse-tree "treesit.c")
35(declare-function treesit-node-child "treesit.c")
36(declare-function treesit-node-start "treesit.c")
37(declare-function treesit-node-type "treesit.c")
38
39(defvar dockerfile-ts-mode--syntax-table
40 (let ((table (make-syntax-table)))
41 (modify-syntax-entry ?# "<" table)
42 (modify-syntax-entry ?\n ">" table)
43 table)
44 "Syntax table for `dockerfile-ts-mode'.")
45
46(defvar dockerfile-ts-mode--indent-rules
47 `((dockerfile
48 ((parent-is "copy_instruction") (nth-sibling 1) 0)
49 ((parent-is "env_instruction") (nth-sibling 1) 0)
50 ((parent-is "expose_instruction") (nth-sibling 1) 0)
51 ((parent-is "label_instruction") (nth-sibling 1) 0)
52 ((parent-is "shell_command") first-sibling 0)
53 ((parent-is "string_array") first-sibling 1)))
54 "Tree-sitter indent rules.")
55
56(defvar dockerfile-ts-mode--keywords
57 '("ADD" "ARG" "AS" "CMD" "COPY" "CROSS_BUILD" "ENTRYPOINT" "ENV"
58 "EXPOSE" "FROM" "HEALTHCHECK" "LABEL" "MAINTAINER" "ONBUILD" "RUN"
59 "SHELL" "STOPSIGNAL" "USER" "VOLUME" "WORKDIR")
60 "Dockerfile keywords for tree-sitter font-locking.")
61
62(defvar dockerfile-ts-mode--font-lock-settings
63 (treesit-font-lock-rules
64 :language 'dockerfile
65 :feature 'bracket
66 '((["[" "]"]) @font-lock-bracket-face)
67
68 :language 'dockerfile
69 :feature 'comment
70 '((comment) @font-lock-comment-face)
71
72 :language 'dockerfile
73 :feature 'delimiter
74 '(([","]) @font-lock-delimiter-face)
75
76 :language 'dockerfile
77 :feature 'image-spec
78 '((image_spec) @font-lock-constant-face)
79
80 :language 'dockerfile
81 :feature 'keyword
82 `([,@dockerfile-ts-mode--keywords] @font-lock-keyword-face)
83
84 :language 'dockerfile
85 :feature 'number
86 '((expose_port) @font-lock-number-face)
87
88 :language 'dockerfile
89 :feature 'operator
90 '((["="]) @font-lock-operator-face)
91
92 :language 'dockerfile
93 :feature 'string
94 '((double_quoted_string) @font-lock-string-face)
95
96 :language 'dockerfile
97 :feature 'error
98 :override t
99 '((ERROR) @font-lock-warning-face))
100 "Tree-sitter font-lock settings.")
101
102(defun dockerfile-ts-mode--imenu ()
103 "Return Imenu alist for the current buffer."
104 (let* ((node (treesit-buffer-root-node))
105 (stage-tree (treesit-induce-sparse-tree
106 node "from_instruction"
107 nil 1000)))
108 `(("Stage" . ,(dockerfile-ts-mode--imenu-1 stage-tree)))))
109
110(defun dockerfile-ts-mode--imenu-1 (node)
111 "Helper for `dockerfile-ts-mode--imenu'.
112Find string representation for NODE and set marker, then recurse
113the subtrees."
114 (let* ((ts-node (car node))
115 (children (cdr node))
116 (subtrees (mapcan #'dockerfile-ts-mode--imenu-1
117 children))
118 (name (when ts-node
119 (pcase (treesit-node-type ts-node)
120 ("from_instruction" (treesit-node-text
121 (treesit-node-child ts-node 1) t)))))
122 (marker (when ts-node
123 (set-marker (make-marker)
124 (treesit-node-start ts-node)))))
125 (cond
126 ((or (null ts-node) (null name)) subtrees)
127 (subtrees
128 `((,name ,(cons name marker) ,@subtrees)))
129 (t
130 `((,name . ,marker))))))
131
132;;;###autoload
133(add-to-list 'auto-mode-alist
134 `(,(rx (|
135 (: "Dockerfile" (? "." (* nonl)))
136 (: "." (any "dD") "ockerfile"))
137 eol)
138 . dockerfile-ts-mode))
139
140;;;###autoload
141(define-derived-mode dockerfile-ts-mode prog-mode "Dockerfile"
142 "Major mode for editing Dockerfiles, powered by tree-sitter."
143 :group 'dockerfile
144 :syntax-table dockerfile-ts-mode--syntax-table
145
146 (when (treesit-ready-p 'dockerfile)
147 (treesit-parser-create 'dockerfile)
148
149 ;; Comments.
150 (setq-local comment-start "# ")
151 (setq-local comment-end "")
152 (setq-local comment-start-skip (rx "#" (* (syntax whitespace))))
153
154 ;; Imenu.
155 (setq-local imenu-create-index-function
156 #'dockerfile-ts-mode--imenu)
157 (setq-local which-func-functions nil)
158
159 ;; Indent.
160 (setq-local treesit-simple-indent-rules
161 dockerfile-ts-mode--indent-rules)
162
163 ;; Font-lock.
164 (setq-local treesit-font-lock-settings
165 dockerfile-ts-mode--font-lock-settings)
166 (setq-local treesit-font-lock-feature-list
167 '((comment)
168 (keyword string)
169 (image-spec number)
170 (bracket delimiter error operator)))
171
172 (treesit-major-mode-setup)))
173
174(provide 'dockerfile-ts-mode)
175
176;;; 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)."
236 . ,(eglot-alternatives '(("vscode-css-language-server" "--stdio") 236 . ,(eglot-alternatives '(("vscode-css-language-server" "--stdio")
237 ("css-languageserver" "--stdio")))) 237 ("css-languageserver" "--stdio"))))
238 (html-mode . ,(eglot-alternatives '(("vscode-html-language-server" "--stdio") ("html-languageserver" "--stdio")))) 238 (html-mode . ,(eglot-alternatives '(("vscode-html-language-server" "--stdio") ("html-languageserver" "--stdio"))))
239 (dockerfile-mode . ("docker-langserver" "--stdio")) 239 ((dockerfile-mode dockerfile-ts-mode) . ("docker-langserver" "--stdio"))
240 ((clojure-mode clojurescript-mode clojurec-mode) 240 ((clojure-mode clojurescript-mode clojurec-mode)
241 . ("clojure-lsp")) 241 . ("clojure-lsp"))
242 ((csharp-mode csharp-ts-mode) 242 ((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."
321 (append "{}():;," electric-indent-chars)) 321 (append "{}():;," electric-indent-chars))
322 322
323 ;; Navigation. 323 ;; Navigation.
324 (setq-local treesit-defun-type-regexp "declaration") 324 (setq-local treesit-defun-type-regexp
325 (regexp-opt '("method_declaration"
326 "class_declaration"
327 "record_declaration"
328 "interface_declaration"
329 "enum_declaration"
330 "import_declaration"
331 "package_declaration"
332 "module_declaration")))
325 333
326 ;; Font-lock. 334 ;; Font-lock.
327 (setq-local treesit-font-lock-settings java-ts-mode--font-lock-settings) 335 (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
@@ -3545,6 +3545,20 @@ This function is intended for use in `after-change-functions'."
3545 3545
3546 :language 'javascript 3546 :language 'javascript
3547 :override t 3547 :override t
3548 :feature 'property
3549 ;; This needs to be before function-name feature, because methods
3550 ;; can be both property and function-name, and we want them in
3551 ;; function-name face.
3552 `((property_identifier) @font-lock-property-face
3553
3554 (pair value: (identifier) @font-lock-variable-name-face)
3555
3556 ((shorthand_property_identifier) @font-lock-property-face)
3557
3558 ((shorthand_property_identifier_pattern) @font-lock-property-face))
3559
3560 :language 'javascript
3561 :override t
3548 :feature 'expression 3562 :feature 'expression
3549 `((assignment_expression 3563 `((assignment_expression
3550 left: [(identifier) @font-lock-function-name-face 3564 left: [(identifier) @font-lock-function-name-face
@@ -3611,18 +3625,7 @@ This function is intended for use in `after-change-functions'."
3611 :language 'javascript 3625 :language 'javascript
3612 :feature 'escape-sequence 3626 :feature 'escape-sequence
3613 :override t 3627 :override t
3614 '((escape_sequence) @font-lock-escape-face) 3628 '((escape_sequence) @font-lock-escape-face))
3615
3616 :language 'javascript
3617 :override t
3618 :feature 'property
3619 `((property_identifier) @font-lock-property-face
3620
3621 (pair value: (identifier) @font-lock-variable-name-face)
3622
3623 ((shorthand_property_identifier) @font-lock-property-face)
3624
3625 ((shorthand_property_identifier_pattern) @font-lock-property-face)))
3626 "Tree-sitter font-lock settings.") 3629 "Tree-sitter font-lock settings.")
3627 3630
3628(defun js--fontify-template-string (node override start end &rest _) 3631(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'.")
515 (marker-re 515 (marker-re
516 (mapconcat 516 (mapconcat
517 (lambda (m) (format "\\(%s\\)" (wildcard-to-regexp m))) 517 (lambda (m) (format "\\(%s\\)" (wildcard-to-regexp m)))
518 (append backend-markers project-vc-extra-root-markers) 518 (append backend-markers
519 (project--value-in-dir 'project-vc-extra-root-markers dir))
519 "\\|")) 520 "\\|"))
520 (locate-dominating-stop-dir-regexp 521 (locate-dominating-stop-dir-regexp
521 (or vc-ignore-dir-regexp locate-dominating-stop-dir-regexp)) 522 (or vc-ignore-dir-regexp locate-dominating-stop-dir-regexp))
@@ -535,7 +536,7 @@ project backend implementation of `project-external-roots'.")
535 project) 536 project)
536 (when (and 537 (when (and
537 (eq backend 'Git) 538 (eq backend 'Git)
538 project-vc-merge-submodules 539 (project--vc-merge-submodules-p root)
539 (project--submodule-p root)) 540 (project--submodule-p root))
540 (let* ((parent (file-name-directory (directory-file-name root)))) 541 (let* ((parent (file-name-directory (directory-file-name root))))
541 (setq root (vc-call-backend 'Git 'root parent)))) 542 (setq root (vc-call-backend 'Git 'root parent))))
@@ -582,7 +583,7 @@ project backend implementation of `project-external-roots'.")
582(cl-defmethod project-files ((project (head vc)) &optional dirs) 583(cl-defmethod project-files ((project (head vc)) &optional dirs)
583 (mapcan 584 (mapcan
584 (lambda (dir) 585 (lambda (dir)
585 (let ((ignores project-vc-ignores) 586 (let ((ignores (project--value-in-dir 'project-vc-ignores (nth 2 project)))
586 (backend (cadr project))) 587 (backend (cadr project)))
587 (when backend 588 (when backend
588 (require (intern (concat "vc-" (downcase (symbol-name backend)))))) 589 (require (intern (concat "vc-" (downcase (symbol-name backend))))))
@@ -608,13 +609,16 @@ project backend implementation of `project-external-roots'.")
608 (defvar vc-git-use-literal-pathspecs) 609 (defvar vc-git-use-literal-pathspecs)
609 (pcase backend 610 (pcase backend
610 (`Git 611 (`Git
611 (let ((default-directory (expand-file-name (file-name-as-directory dir))) 612 (let* ((default-directory (expand-file-name (file-name-as-directory dir)))
612 (args '("-z")) 613 (args '("-z"))
613 (vc-git-use-literal-pathspecs nil) 614 (vc-git-use-literal-pathspecs nil)
614 files) 615 (include-untracked (project--value-in-dir
616 'project-vc-include-untracked
617 dir))
618 files)
615 (setq args (append args 619 (setq args (append args
616 '("-c" "--exclude-standard") 620 '("-c" "--exclude-standard")
617 (and project-vc-include-untracked '("-o")))) 621 (and include-untracked '("-o"))))
618 (when extra-ignores 622 (when extra-ignores
619 (setq args (append args 623 (setq args (append args
620 (cons "--" 624 (cons "--"
@@ -647,7 +651,7 @@ project backend implementation of `project-external-roots'.")
647 (split-string 651 (split-string
648 (apply #'vc-git--run-command-string nil "ls-files" args) 652 (apply #'vc-git--run-command-string nil "ls-files" args)
649 "\0" t))) 653 "\0" t)))
650 (when project-vc-merge-submodules 654 (when (project--vc-merge-submodules-p default-directory)
651 ;; Unfortunately, 'ls-files --recurse-submodules' conflicts with '-o'. 655 ;; Unfortunately, 'ls-files --recurse-submodules' conflicts with '-o'.
652 (let* ((submodules (project--git-submodules)) 656 (let* ((submodules (project--git-submodules))
653 (sub-files 657 (sub-files
@@ -665,10 +669,13 @@ project backend implementation of `project-external-roots'.")
665 ;; XXX: Better solutions welcome, but this seems cheap enough. 669 ;; XXX: Better solutions welcome, but this seems cheap enough.
666 (delete-consecutive-dups files))) 670 (delete-consecutive-dups files)))
667 (`Hg 671 (`Hg
668 (let ((default-directory (expand-file-name (file-name-as-directory dir))) 672 (let* ((default-directory (expand-file-name (file-name-as-directory dir)))
669 (args (list (concat "-mcard" (and project-vc-include-untracked "u")) 673 (include-untracked (project--value-in-dir
670 "--no-status" 674 'project-vc-include-untracked
671 "-0"))) 675 dir))
676 (args (list (concat "-mcard" (and include-untracked "u"))
677 "--no-status"
678 "-0")))
672 (when extra-ignores 679 (when extra-ignores
673 (setq args (nconc args 680 (setq args (nconc args
674 (mapcan 681 (mapcan
@@ -681,6 +688,11 @@ project backend implementation of `project-external-roots'.")
681 (lambda (s) (concat default-directory s)) 688 (lambda (s) (concat default-directory s))
682 (split-string (buffer-string) "\0" t))))))) 689 (split-string (buffer-string) "\0" t)))))))
683 690
691(defun project--vc-merge-submodules-p (dir)
692 (project--value-in-dir
693 'project-vc-merge-submodules
694 dir))
695
684(defun project--git-submodules () 696(defun project--git-submodules ()
685 ;; 'git submodule foreach' is much slower. 697 ;; 'git submodule foreach' is much slower.
686 (condition-case nil 698 (condition-case nil
@@ -722,7 +734,7 @@ project backend implementation of `project-external-roots'.")
722 (condition-case nil 734 (condition-case nil
723 (vc-call-backend backend 'ignore-completion-table root) 735 (vc-call-backend backend 'ignore-completion-table root)
724 (vc-not-supported () nil))))) 736 (vc-not-supported () nil)))))
725 project-vc-ignores 737 (project--value-in-dir 'project-vc-ignores root)
726 (mapcar 738 (mapcar
727 (lambda (dir) 739 (lambda (dir)
728 (concat dir "/")) 740 (concat dir "/"))
@@ -753,9 +765,16 @@ DIRS must contain directory names."
753 ;; Sidestep the issue of expanded/abbreviated file names here. 765 ;; Sidestep the issue of expanded/abbreviated file names here.
754 (cl-set-difference files dirs :test #'file-in-directory-p)) 766 (cl-set-difference files dirs :test #'file-in-directory-p))
755 767
768(defun project--value-in-dir (var dir)
769 (with-temp-buffer
770 (setq default-directory dir)
771 (let ((enable-local-variables :all))
772 (hack-dir-local-variables-non-file-buffer))
773 (symbol-value var)))
774
756(cl-defmethod project-buffers ((project (head vc))) 775(cl-defmethod project-buffers ((project (head vc)))
757 (let* ((root (expand-file-name (file-name-as-directory (project-root project)))) 776 (let* ((root (expand-file-name (file-name-as-directory (project-root project))))
758 (modules (unless (or project-vc-merge-submodules 777 (modules (unless (or (project--vc-merge-submodules-p root)
759 (project--submodule-p root)) 778 (project--submodule-p root))
760 (mapcar 779 (mapcar
761 (lambda (m) (format "%s%s/" root m)) 780 (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'."
89 ((parent-is "object") parent-bol typescript-ts-mode-indent-offset) 89 ((parent-is "object") parent-bol typescript-ts-mode-indent-offset)
90 ((parent-is "object_type") parent-bol typescript-ts-mode-indent-offset) 90 ((parent-is "object_type") parent-bol typescript-ts-mode-indent-offset)
91 ((parent-is "enum_body") parent-bol typescript-ts-mode-indent-offset) 91 ((parent-is "enum_body") parent-bol typescript-ts-mode-indent-offset)
92 ((parent-is "class_body") parent-bol typescript-ts-mode-indent-offset)
92 ((parent-is "arrow_function") parent-bol typescript-ts-mode-indent-offset) 93 ((parent-is "arrow_function") parent-bol typescript-ts-mode-indent-offset)
93 ((parent-is "parenthesized_expression") parent-bol typescript-ts-mode-indent-offset) 94 ((parent-is "parenthesized_expression") parent-bol typescript-ts-mode-indent-offset)
94 95
@@ -361,8 +362,7 @@ Argument LANGUAGE is either `typescript' or `tsx'."
361 (setq-local treesit-font-lock-settings 362 (setq-local treesit-font-lock-settings
362 (typescript-ts-mode--font-lock-settings 'typescript)) 363 (typescript-ts-mode--font-lock-settings 'typescript))
363 (setq-local treesit-font-lock-feature-list 364 (setq-local treesit-font-lock-feature-list
364 '((comment declaration) 365 '((comment declaration keyword string escape-sequence)
365 (keyword string)
366 (constant expression identifier number pattern property) 366 (constant expression identifier number pattern property)
367 (bracket delimiter))) 367 (bracket delimiter)))
368 368
@@ -396,8 +396,7 @@ Argument LANGUAGE is either `typescript' or `tsx'."
396 (setq-local treesit-font-lock-settings 396 (setq-local treesit-font-lock-settings
397 (typescript-ts-mode--font-lock-settings 'tsx)) 397 (typescript-ts-mode--font-lock-settings 'tsx))
398 (setq-local treesit-font-lock-feature-list 398 (setq-local treesit-font-lock-feature-list
399 '((comment declaration) 399 '((comment declaration keyword string escape-sequence)
400 (keyword string)
401 (constant expression identifier jsx number pattern property) 400 (constant expression identifier jsx number pattern property)
402 (bracket delimiter))) 401 (bracket delimiter)))
403 402
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)
384 mysteriously drops. 3) what if a user uses so many stuff that the 384 mysteriously drops. 3) what if a user uses so many stuff that the
385 default cache size (20) is not enough and we end up thrashing? 385 default cache size (20) is not enough and we end up thrashing?
386 These are all imaginary scenarios but they are not impossible 386 These are all imaginary scenarios but they are not impossible
387 :-) */ 387 :-)
388
389 Parsers in indirect buffers: We make indirect buffers to share the
390 parser of its base buffer. Indirect buffers and their base buffer
391 share the same buffer content but not other buffer attributes. If
392 they have separate parser lists, changes made in an indirect buffer
393 will only update parsers of that indirect buffer, and not parsers
394 in the base buffer or other indirect buffers, and vice versa. We
395 could keep track of all the base and indirect buffers, and update
396 all of their parsers, but ultimately decide to take a simpler
397 approach, which is to make indirect buffers share their base
398 buffer's parser list. The discussion can be found in bug#59693. */
388 399
389 400
390/*** Initialization */ 401/*** Initialization */
@@ -697,9 +708,10 @@ void
697treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte, 708treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte,
698 ptrdiff_t new_end_byte) 709 ptrdiff_t new_end_byte)
699{ 710{
700 Lisp_Object parser_list; 711 struct buffer *base_buffer = current_buffer;
701 712 if (current_buffer->base_buffer)
702 parser_list = BVAR (current_buffer, ts_parser_list); 713 base_buffer = current_buffer->base_buffer;
714 Lisp_Object parser_list = BVAR (base_buffer, ts_parser_list);
703 715
704 FOR_EACH_TAIL_SAFE (parser_list) 716 FOR_EACH_TAIL_SAFE (parser_list)
705 { 717 {
@@ -1252,12 +1264,16 @@ DEFUN ("treesit-parser-create",
1252 1, 3, 0, 1264 1, 3, 0,
1253 doc: /* Create and return a parser in BUFFER for LANGUAGE. 1265 doc: /* Create and return a parser in BUFFER for LANGUAGE.
1254 1266
1255The parser is automatically added to BUFFER's parser list, as 1267The parser is automatically added to BUFFER's parser list, as returned
1256returned by `treesit-parser-list'. 1268by `treesit-parser-list'. LANGUAGE is a language symbol. If BUFFER
1257LANGUAGE is a language symbol. If BUFFER is nil or omitted, it 1269is nil or omitted, it defaults to the current buffer. If BUFFER
1258defaults to the current buffer. If BUFFER already has a parser for 1270already has a parser for LANGUAGE, return that parser, but if NO-REUSE
1259LANGUAGE, return that parser, but if NO-REUSE is non-nil, always 1271is non-nil, always create a new parser.
1260create a new parser. */) 1272
1273If that buffer is an indirect buffer, its base buffer is used instead.
1274That is, indirect buffers use their base buffer's parsers. Lisp
1275programs should widen as necessary should they want to use a parser in
1276an indirect buffer. */)
1261 (Lisp_Object language, Lisp_Object buffer, Lisp_Object no_reuse) 1277 (Lisp_Object language, Lisp_Object buffer, Lisp_Object no_reuse)
1262{ 1278{
1263 treesit_initialize (); 1279 treesit_initialize ();
@@ -1271,16 +1287,21 @@ create a new parser. */)
1271 CHECK_BUFFER (buffer); 1287 CHECK_BUFFER (buffer);
1272 buf = XBUFFER (buffer); 1288 buf = XBUFFER (buffer);
1273 } 1289 }
1290 if (buf->base_buffer)
1291 buf = buf->base_buffer;
1292
1274 treesit_check_buffer_size (buf); 1293 treesit_check_buffer_size (buf);
1275 1294
1276 /* See if we can reuse a parser. */ 1295 /* See if we can reuse a parser. */
1277 for (Lisp_Object tail = BVAR (buf, ts_parser_list); 1296 if (NILP (no_reuse))
1278 NILP (no_reuse) && !NILP (tail);
1279 tail = XCDR (tail))
1280 { 1297 {
1281 struct Lisp_TS_Parser *parser = XTS_PARSER (XCAR (tail)); 1298 Lisp_Object tail = BVAR (buf, ts_parser_list);
1282 if (EQ (parser->language_symbol, language)) 1299 FOR_EACH_TAIL (tail)
1283 return XCAR (tail); 1300 {
1301 struct Lisp_TS_Parser *parser = XTS_PARSER (XCAR (tail));
1302 if (EQ (parser->language_symbol, language))
1303 return XCAR (tail);
1304 }
1284 } 1305 }
1285 1306
1286 /* Load language. */ 1307 /* Load language. */
@@ -1329,7 +1350,10 @@ DEFUN ("treesit-parser-list",
1329 Ftreesit_parser_list, Streesit_parser_list, 1350 Ftreesit_parser_list, Streesit_parser_list,
1330 0, 1, 0, 1351 0, 1, 0,
1331 doc: /* Return BUFFER's parser list. 1352 doc: /* Return BUFFER's parser list.
1332BUFFER defaults to the current buffer. */) 1353
1354BUFFER defaults to the current buffer. If that buffer is an indirect
1355buffer, its base buffer is used instead. That is, indirect buffers
1356use their base buffer's parsers. */)
1333 (Lisp_Object buffer) 1357 (Lisp_Object buffer)
1334{ 1358{
1335 struct buffer *buf; 1359 struct buffer *buf;
@@ -1340,6 +1364,9 @@ BUFFER defaults to the current buffer. */)
1340 CHECK_BUFFER (buffer); 1364 CHECK_BUFFER (buffer);
1341 buf = XBUFFER (buffer); 1365 buf = XBUFFER (buffer);
1342 } 1366 }
1367 if (buf->base_buffer)
1368 buf = buf->base_buffer;
1369
1343 /* Return a fresh list so messing with that list doesn't affect our 1370 /* Return a fresh list so messing with that list doesn't affect our
1344 internal data. */ 1371 internal data. */
1345 Lisp_Object return_list = Qnil; 1372 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)."
41 (skip-unless (executable-find "grep")) 41 (skip-unless (executable-find "grep"))
42 (ert-with-temp-directory directory 42 (ert-with-temp-directory directory
43 (let ((default-directory directory) 43 (let ((default-directory directory)
44 (project-current-inhibit-prompt t) 44 (project-current-directory-override t)
45 (project-find-functions nil) 45 (project-find-functions nil)
46 (project-list-file 46 (project-list-file
47 (expand-file-name "projects" directory)) 47 (expand-file-name "projects" directory))
@@ -139,4 +139,17 @@ When `project-ignores' includes a name matching project dir."
139 (should-not (null project)) 139 (should-not (null project))
140 (should (string-match-p "/test/lisp/\\'" (project-root project))))) 140 (should (string-match-p "/test/lisp/\\'" (project-root project)))))
141 141
142(ert-deftest project-vc-supports-project-in-different-dir ()
143 "Check that it picks up dir-locals settings from somewhere else."
144 (skip-unless (eq (vc-responsible-backend default-directory) 'Git))
145 (let* ((dir (ert-resource-directory))
146 (_ (vc-file-clearprops dir))
147 (project-vc-extra-root-markers '(".dir-locals.el"))
148 (project (project-current nil dir)))
149 (should-not (null project))
150 (should (string-match-p "/test/lisp/progmodes/project-resources/\\'" (project-root project)))
151 (should (member "etc" (project-ignores project dir)))
152 (should (equal '(".dir-locals.el" "foo")
153 (mapcar #'file-name-nondirectory (project-files project))))))
154
142;;; project-tests.el ends here 155;;; 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 @@
161 (should (treesit-node-eq root-node root-node)) 161 (should (treesit-node-eq root-node root-node))
162 (should (not (treesit-node-eq root-node doc-node)))))) 162 (should (not (treesit-node-eq root-node doc-node))))))
163 163
164(ert-deftest treesit-indirect-buffer ()
165 "Tests for indirect buffers."
166 (skip-unless (treesit-language-available-p 'json))
167 (let ((base (get-buffer-create "*treesit test*"))
168 parser indirect)
169 (unwind-protect
170 (progn
171 (with-current-buffer base
172 (setq indirect (clone-indirect-buffer "*treesit test 1*" nil)))
173 (with-current-buffer indirect
174 (setq parser (treesit-parser-create 'json)))
175 ;; 1. Parser created in the indirect buffer should be
176 ;; actually be created in the base buffer.
177 (with-current-buffer base
178 (should (equal (list parser)
179 (treesit-parser-list)))
180 (insert "[1,2,3]"))
181 ;; Change in the base buffer should be reflected in the
182 ;; indirect buffer.
183 (with-current-buffer indirect
184 (should (eq (treesit-node-end
185 (treesit-buffer-root-node))
186 8))
187 (erase-buffer))
188 ;; Change in the indirect buffer should be reflected in the
189 ;; base buffer.
190 (with-current-buffer base
191 (should (eq (treesit-node-end
192 (treesit-buffer-root-node))
193 1))
194 (erase-buffer)))
195 (kill-buffer base)
196 (kill-buffer indirect))))
197
164(ert-deftest treesit-query-api () 198(ert-deftest treesit-query-api ()
165 "Tests for query API." 199 "Tests for query API."
166 (skip-unless (treesit-language-available-p 'json)) 200 (skip-unless (treesit-language-available-p 'json))