diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 5cc4c1e7ddf..ad01e0f2886 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -3390,7 +3390,8 @@ first, before handlers for jobs such as remote file access. @code{file-readable-p}, @code{file-regular-p}, @code{file-remote-p}, @code{file-selinux-context}, @code{file-symlink-p}, @code{file-system-info}, -@code{file-truename}, @code{file-writable-p}, +@code{file-truename}, @code{file-user-uid}, +@code{file-writable-p}, @code{find-backup-file-name},@* @code{get-file-buffer}, @code{insert-directory}, @@ -3451,7 +3452,8 @@ first, before handlers for jobs such as remote file access. @code{file-readable-p}, @code{file-regular-p}, @code{file-remote-p}, @code{file-selinux-context}, @code{file-symlink-p}, @code{file-system-info}, -@code{file-truename}, @code{file-writable-p}, +@code{file-truename}, @code{file-user-uid}, +@code{file-writable-p}, @code{find-backup-file-name}, @code{get-file-buffer}, @code{insert-directory}, diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi index 094e954e82b..53ca1285827 100644 --- a/doc/lispref/os.texi +++ b/doc/lispref/os.texi @@ -1280,6 +1280,16 @@ This function returns the real @acronym{UID} of the user. This function returns the effective @acronym{UID} of the user. @end defun +@defun file-user-uid +This function returns the connection-local value for the user's +effective @acronym{UID}. If @code{default-directory} is local, this +is equivalent to @code{user-uid}, but for remote files (@pxref{Remote +Files, , , emacs, The GNU Emacs Manual}), it will return the +@acronym{UID} for the user associated with that remote connection; if +the remote connection has no associated user, it will instead return +-1. +@end defun + @cindex GID @defun group-gid This function returns the effective @acronym{GID} of the Emacs process. diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi index fc7d52eb711..c40ff58f42c 100644 --- a/doc/misc/eshell.texi +++ b/doc/misc/eshell.texi @@ -983,6 +983,13 @@ whenever you change the current directory to a different host the value will automatically update to reflect the search path on that host. +@vindex $UID +@item $UID +This returns the effective @acronym{UID} for the current user. This +variable is connection-aware, so when the current directory is remote, +its value will be @acronym{UID} for the user associated with that +remote connection. + @vindex $_ @item $_ This refers to the last argument of the last command. With a @@ -1014,6 +1021,7 @@ that are currently visible in the Eshell window. They are both copied to the environment, so external commands invoked from Eshell can consult them to do the right thing. +@vindex $INSIDE_EMACS @item $INSIDE_EMACS This variable indicates to external commands that they are being invoked from within Emacs so they can adjust their behavior if diff --git a/etc/NEWS b/etc/NEWS index 068f7a27db8..cde6783349f 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -235,6 +235,12 @@ compared reliably at all. This warning can be suppressed using 'with-suppressed-warnings' with the warning name 'suspicious'. ++++ +** New function 'file-user-uid'. +This function is like 'user-uid', but is aware of file name handlers, +so it will return the remote UID for remote files (or -1 if the +connection has no associated user). + * Changes in Emacs 30.1 on Non-Free Operating Systems diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index d7a0d851e01..039cebedb44 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -1380,6 +1380,9 @@ See Info node `(elisp) Integer Basics'." ;; (apply F ... (list X Y ...)) -> (funcall F ... X Y ...) ((eq (car-safe last) 'list) `(funcall ,fn ,@(butlast (cddr form)) ,@(cdr last))) + ;; (apply F ... (cons X Y)) -> (apply F ... X Y) + ((eq (car-safe last) 'cons) + (append (butlast form) (cdr last))) (t form))) form))) diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index 23d02ba92cf..fb4b73b1c14 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el @@ -1575,7 +1575,7 @@ extra args." "`%s' called with %d args to fill %d format field(s)" (car form) nargs nfields))))) -(dolist (elt '(format message error)) +(dolist (elt '(format message format-message error)) (put elt 'byte-compile-format-like t)) (defun byte-compile--suspicious-defcustom-choice (type) diff --git a/lisp/eshell/em-prompt.el b/lisp/eshell/em-prompt.el index 52d46282c52..b3a0fadf618 100644 --- a/lisp/eshell/em-prompt.el +++ b/lisp/eshell/em-prompt.el @@ -52,7 +52,7 @@ as is common with most shells." (defcustom eshell-prompt-function (lambda () (concat (abbreviate-file-name (eshell/pwd)) - (if (= (user-uid) 0) " # " " $ "))) + (if (= (file-user-uid) 0) " # " " $ "))) "A function that returns the Eshell prompt string. Make sure to update `eshell-prompt-regexp' so that it will match your prompt." diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el index 811bb9957cf..fd76a2c6f09 100644 --- a/lisp/eshell/esh-var.el +++ b/lisp/eshell/esh-var.el @@ -162,6 +162,7 @@ if they are quoted with a backslash." ("COLUMNS" ,(lambda () (window-body-width nil 'remap)) t t) ("LINES" ,(lambda () (window-body-height nil 'remap)) t t) ("INSIDE_EMACS" eshell-inside-emacs t) + ("UID" ,(lambda () (file-user-uid)) nil t) ;; for esh-ext.el ("PATH" (,(lambda () (string-join (eshell-get-path t) (path-separator))) diff --git a/lisp/htmlfontify.el b/lisp/htmlfontify.el index 0959405081f..1ab33cc6411 100644 --- a/lisp/htmlfontify.el +++ b/lisp/htmlfontify.el @@ -373,13 +373,13 @@ otherwise." :type '(string)) (defcustom hfy-exclude-file-rules - '("\\.flc$" - "/CVS/.*" - ".*~$" - "/\\.git\\(?:/.*\\)?$") - "Define some regular expressions to exclude files" + '("\\.flc\\'" + "/CVS/" + "~\\'" + "/\\.git\\(?:/\\|\\'\\)") + "Regular expressions matching files to exclude." :tag "exclude-rules" - :type '(list string) + :type '(repeat regexp) :version "29.1") (defcustom hfy-display-class nil @@ -1835,7 +1835,7 @@ Strips any leading \"./\" from each filename." (seq-some (lambda (r) (string-match r f)) hfy-exclude-file-rules))) - (directory-files-recursively "." ".*" nil t))) + (directory-files-recursively "." "" nil t))) ;; strip the filename off, return a directory name ;; not a particularly thorough implementation, but it will be diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el index 29b9f894ec3..10f33e5f929 100644 --- a/lisp/net/tramp-adb.el +++ b/lisp/net/tramp-adb.el @@ -153,6 +153,7 @@ It is used for TCP/IP devices." (file-symlink-p . tramp-handle-file-symlink-p) (file-system-info . tramp-adb-handle-file-system-info) (file-truename . tramp-handle-file-truename) + (file-user-uid . tramp-handle-file-user-uid) (file-writable-p . tramp-adb-handle-file-writable-p) (find-backup-file-name . tramp-handle-find-backup-file-name) ;; `get-file-buffer' performed by default handler. diff --git a/lisp/net/tramp-archive.el b/lisp/net/tramp-archive.el index 3b1d568c127..b9cf85bd843 100644 --- a/lisp/net/tramp-archive.el +++ b/lisp/net/tramp-archive.el @@ -265,6 +265,7 @@ It must be supported by libarchive(3).") (file-symlink-p . tramp-handle-file-symlink-p) (file-system-info . tramp-archive-handle-file-system-info) (file-truename . tramp-archive-handle-file-truename) + (file-user-uid . tramp-archive-handle-file-user-uid) (file-writable-p . ignore) (find-backup-file-name . ignore) ;; `get-file-buffer' performed by default handler. @@ -701,6 +702,12 @@ offered." (let ((default-directory (file-name-directory archive))) (temporary-file-directory)))) +(defun tramp-archive-handle-file-user-uid () + "Like `user-uid' for file archives." + (with-parsed-tramp-archive-file-name default-directory nil + (let ((default-directory (file-name-directory archive))) + (file-user-uid)))) + (defun tramp-archive-handle-not-implemented (operation &rest args) "Generic handler for operations not implemented for file archives." (let ((v (ignore-errors diff --git a/lisp/net/tramp-crypt.el b/lisp/net/tramp-crypt.el index 507fd432419..b9d0d96eccc 100644 --- a/lisp/net/tramp-crypt.el +++ b/lisp/net/tramp-crypt.el @@ -204,6 +204,7 @@ If NAME doesn't belong to an encrypted remote directory, return nil." (file-symlink-p . tramp-handle-file-symlink-p) (file-system-info . tramp-crypt-handle-file-system-info) ;; `file-truename' performed by default handler. + (file-user-uid . tramp-handle-file-user-uid) (file-writable-p . tramp-crypt-handle-file-writable-p) (find-backup-file-name . tramp-handle-find-backup-file-name) ;; `get-file-buffer' performed by default handler. diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el index ea774d0b9b7..02ceb2979f7 100644 --- a/lisp/net/tramp-gvfs.el +++ b/lisp/net/tramp-gvfs.el @@ -798,6 +798,7 @@ It has been changed in GVFS 1.14.") (file-symlink-p . tramp-handle-file-symlink-p) (file-system-info . tramp-gvfs-handle-file-system-info) (file-truename . tramp-handle-file-truename) + (file-user-uid . tramp-handle-file-user-uid) (file-writable-p . tramp-handle-file-writable-p) (find-backup-file-name . tramp-handle-find-backup-file-name) ;; `get-file-buffer' performed by default handler. diff --git a/lisp/net/tramp-rclone.el b/lisp/net/tramp-rclone.el index b28f9f64952..9eb2a54cdcf 100644 --- a/lisp/net/tramp-rclone.el +++ b/lisp/net/tramp-rclone.el @@ -118,6 +118,7 @@ (file-symlink-p . tramp-handle-file-symlink-p) (file-system-info . tramp-rclone-handle-file-system-info) (file-truename . tramp-handle-file-truename) + (file-user-uid . tramp-handle-file-user-uid) (file-writable-p . tramp-handle-file-writable-p) (find-backup-file-name . tramp-handle-find-backup-file-name) ;; `get-file-buffer' performed by default handler. diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index acbb6aea135..46b1f612101 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -1086,6 +1086,7 @@ Format specifiers \"%s\" are replaced before the script is used.") (file-symlink-p . tramp-handle-file-symlink-p) (file-system-info . tramp-sh-handle-file-system-info) (file-truename . tramp-sh-handle-file-truename) + (file-user-uid . tramp-handle-file-user-uid) (file-writable-p . tramp-sh-handle-file-writable-p) (find-backup-file-name . tramp-handle-find-backup-file-name) ;; `get-file-buffer' performed by default handler. diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el index b3f0344f818..9d03490f1d5 100644 --- a/lisp/net/tramp-smb.el +++ b/lisp/net/tramp-smb.el @@ -269,6 +269,7 @@ See `tramp-actions-before-shell' for more info.") (file-symlink-p . tramp-handle-file-symlink-p) (file-system-info . tramp-smb-handle-file-system-info) (file-truename . tramp-handle-file-truename) + (file-user-uid . tramp-handle-file-user-uid) (file-writable-p . tramp-smb-handle-file-writable-p) (find-backup-file-name . tramp-handle-find-backup-file-name) ;; `get-file-buffer' performed by default handler. diff --git a/lisp/net/tramp-sshfs.el b/lisp/net/tramp-sshfs.el index 27b2854e451..65c4bf23c38 100644 --- a/lisp/net/tramp-sshfs.el +++ b/lisp/net/tramp-sshfs.el @@ -124,6 +124,7 @@ (file-symlink-p . tramp-handle-file-symlink-p) (file-system-info . tramp-sshfs-handle-file-system-info) (file-truename . tramp-handle-file-truename) + (file-user-uid . tramp-handle-file-user-uid) (file-writable-p . tramp-sshfs-handle-file-writable-p) (find-backup-file-name . tramp-handle-find-backup-file-name) ;; `get-file-buffer' performed by default handler. diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el index ce080918e0f..db7ac842871 100644 --- a/lisp/net/tramp-sudoedit.el +++ b/lisp/net/tramp-sudoedit.el @@ -114,6 +114,7 @@ See `tramp-actions-before-shell' for more info.") (file-symlink-p . tramp-handle-file-symlink-p) (file-system-info . tramp-sudoedit-handle-file-system-info) (file-truename . tramp-sudoedit-handle-file-truename) + (file-user-uid . tramp-handle-file-user-uid) (file-writable-p . tramp-sudoedit-handle-file-writable-p) (find-backup-file-name . tramp-handle-find-backup-file-name) ;; `get-file-buffer' performed by default handler. diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index 169cf1982b4..fab1962d2b7 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -2632,7 +2632,9 @@ Must be handled by the callers." '(exec-path make-nearby-temp-file make-process process-file shell-command start-file-process temporary-file-directory ;; Emacs 29+ only. - list-system-processes memory-info process-attributes)) + list-system-processes memory-info process-attributes + ;; Emacs 30+ only. + file-user-uid)) default-directory) ;; PROC. ((member operation '(file-notify-rm-watch file-notify-valid-p)) @@ -3714,6 +3716,15 @@ Let-bind it when necessary.") vec (concat "~" (substring filename (match-beginning 1)))) (tramp-make-tramp-file-name (tramp-dissect-file-name filename))))) +(defun tramp-handle-file-user-uid () + "Like `user-uid' for Tramp files." + (let ((v (tramp-dissect-file-name default-directory))) + (or (tramp-get-remote-uid v 'integer) + ;; Some handlers for `tramp-get-remote-uid' return nil if they + ;; can't get the UID; always return -1 in this case for + ;; consistency. + -1))) + (defun tramp-handle-access-file (filename string) "Like `access-file' for Tramp files." (setq filename (file-truename filename)) diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index 8ac3ef6808d..45d90ea2431 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -10863,7 +10863,13 @@ This function might do hidden buffer changes." ;; types; other identifiers could just as well be ;; constants in C++. (memq at-type '(known found))))) - (throw 'at-decl-or-cast t) + (progn + ;; The user may be part way through typing a statement + ;; beginning with an identifier. This makes a 'maybe + ;; type in the following "declarator"'s arglist suspect. + (when (eq at-type 'maybe) + (setq unsafe-maybe t)) + (throw 'at-decl-or-cast t)) ;; CASE 7 ;; Can't be a valid declaration or cast, but if we've found a ;; specifier it can't be anything else either, so treat it as diff --git a/lisp/progmodes/dockerfile-ts-mode.el b/lisp/progmodes/dockerfile-ts-mode.el index 3f8766e6713..1b91681f085 100644 --- a/lisp/progmodes/dockerfile-ts-mode.el +++ b/lisp/progmodes/dockerfile-ts-mode.el @@ -161,6 +161,10 @@ the subtrees." (setq-local treesit-simple-indent-rules dockerfile-ts-mode--indent-rules) + ;; Navigation + (setq-local treesit-sentence-type-regexp + "instruction") + ;; Font-lock. (setq-local treesit-font-lock-settings dockerfile-ts-mode--font-lock-settings) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index a846baa1b16..8ce1a8b7baf 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -1075,7 +1075,7 @@ suitable root directory for a given LSP server's purposes." ;;;###autoload (defun eglot (managed-major-mode project class contact language-id - &optional interactive) + &optional _interactive) "Start LSP server in support of PROJECT's buffers under MANAGED-MAJOR-MODE. This starts a Language Server Protocol (LSP) server suitable for the @@ -1112,17 +1112,17 @@ described in `eglot-server-programs', which see. LANGUAGE-ID is the language ID string to send to the server for MANAGED-MAJOR-MODE, which matters to a minority of servers. -INTERACTIVE is t if called interactively." - (interactive (append (eglot--guess-contact t) '(t))) - (setq managed-major-mode (eglot--ensure-list managed-major-mode)) - (let* ((current-server (eglot-current-server)) - (live-p (and current-server (jsonrpc-running-p current-server)))) - (if (and live-p - interactive - (y-or-n-p "[eglot] Live process found, reconnect instead? ")) - (eglot-reconnect current-server interactive) - (when live-p (ignore-errors (eglot-shutdown current-server))) - (eglot--connect managed-major-mode project class contact language-id)))) +INTERACTIVE is ignored and provided for backward compatibility." + (interactive + (let ((current-server (eglot-current-server))) + (unless (or (null current-server) + (y-or-n-p "\ +[eglot] Shut down current connection before attempting new one?")) + (user-error "[eglot] Connection attempt aborted by user.")) + (prog1 (append (eglot--guess-contact t) '(t)) + (when current-server (ignore-errors (eglot-shutdown current-server)))))) + (eglot--connect (eglot--ensure-list managed-major-mode) + project class contact language-id)) (defun eglot-reconnect (server &optional interactive) "Reconnect to SERVER. diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index fa3b4687ef2..28305a0b39b 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el @@ -3454,13 +3454,16 @@ This function is intended for use in `after-change-functions'." ((parent-is "statement_block") parent-bol js-indent-level) ;; JSX - ((node-is "jsx_fragment") parent typescript-ts-mode-indent-offset) - ((node-is "jsx_element") parent typescript-ts-mode-indent-offset) - ((node-is "jsx_expression") parent typescript-ts-mode-indent-offset) - ((node-is "jsx_self_closing_element") parent typescript-ts-mode-indent-offset) + ((match "<" "jsx_fragment") parent 0) + ((parent-is "jsx_fragment") parent js-indent-level) ((node-is "jsx_closing_element") parent 0) - ((node-is "/") parent 0) - ((node-is ">") parent 0))))) + ((node-is "jsx_element") parent js-indent-level) + ((parent-is "jsx_element") parent js-indent-level) + ((parent-is "jsx_opening_element") parent js-indent-level) + ((parent-is "jsx_expression") parent-bol js-indent-level) + ((match "/" "jsx_self_closing_element") parent 0) + ((parent-is "jsx_self_closing_element") parent js-indent-level) + (no-node parent-bol 0))))) (defvar js--treesit-keywords '("as" "async" "await" "break" "case" "catch" "class" "const" "continue" diff --git a/lisp/progmodes/json-ts-mode.el b/lisp/progmodes/json-ts-mode.el index fbcda22acca..f6a303290a8 100644 --- a/lisp/progmodes/json-ts-mode.el +++ b/lisp/progmodes/json-ts-mode.el @@ -147,6 +147,8 @@ Return nil if there is no name or if NODE is not a defun node." (rx (or "pair" "object"))) (setq-local treesit-defun-name-function #'json-ts-mode--defun-name) + (setq-local treesit-sentence-type-regexp "pair") + ;; Font-lock. (setq-local treesit-font-lock-settings json-ts-mode--font-lock-settings) (setq-local treesit-font-lock-feature-list diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index d68b57966ba..939c054b041 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -780,12 +780,20 @@ i.e. expr of def foo(args) = expr is returned." ;; but with node set to the statement and parent set to ;; body_statement for all others. ... Fine. Be that way. ;; Ditto for "block" and "block_body" - ((node-is "body_statement") parent-bol ruby-indent-level) - ((parent-is "body_statement") (ruby-ts--bol ruby-ts--grand-parent-node) ruby-indent-level) - ((match "end" "do_block") parent-bol 0) - ((n-p-gp "block_body" "block" nil) parent-bol ruby-indent-level) - ((n-p-gp nil "block_body" "block") (ruby-ts--bol ruby-ts--grand-parent-node) ruby-indent-level) - ((match "}" "block") parent-bol 0) + ((node-is "body_statement") + (ruby-ts--block-indent-anchor ruby-ts--parent-node) + ruby-indent-level) + ((parent-is "body_statement") + (ruby-ts--block-indent-anchor ruby-ts--grand-parent-node) + ruby-indent-level) + ((match "end" "do_block") (ruby-ts--block-indent-anchor ruby-ts--parent-node) 0) + ((n-p-gp "block_body" "block" nil) + (ruby-ts--block-indent-anchor ruby-ts--parent-node) + ruby-indent-level) + ((n-p-gp nil "block_body" "block") + (ruby-ts--block-indent-anchor ruby-ts--grand-parent-node) + ruby-indent-level) + ((match "}" "block") (ruby-ts--block-indent-anchor ruby-ts--parent-node) 0) ;; Chained strings ((match "string" "chained_string") first-sibling 0) @@ -794,6 +802,18 @@ i.e. expr of def foo(args) = expr is returned." (catch-all parent-bol ruby-indent-level)))) `((ruby . ,common)))) +(defun ruby-ts--block-indent-anchor (block-node-getter) + (lambda (node parent _bol &rest _rest) + (let ((block-node (funcall block-node-getter node parent))) + (save-excursion + (goto-char + (treesit-node-start + (if ruby-block-indent + (ruby-ts--statement-ancestor block-node) + block-node))) + (back-to-indentation) + (point))))) + (defun ruby-ts--class-or-module-p (node) "Predicate if NODE is a class or module." (string-match-p ruby-ts--class-or-module-regex (treesit-node-type node))) diff --git a/lisp/progmodes/typescript-ts-mode.el b/lisp/progmodes/typescript-ts-mode.el index cd631d048ea..f7bf7ed7e42 100644 --- a/lisp/progmodes/typescript-ts-mode.el +++ b/lisp/progmodes/typescript-ts-mode.el @@ -97,13 +97,15 @@ Argument LANGUAGE is either `typescript' or `tsx'." ((parent-is "binary_expression") parent-bol typescript-ts-mode-indent-offset) ,@(when (eq language 'tsx) - `(((node-is "jsx_fragment") parent typescript-ts-mode-indent-offset) - ((node-is "jsx_element") parent typescript-ts-mode-indent-offset) - ((node-is "jsx_expression") parent typescript-ts-mode-indent-offset) - ((node-is "jsx_self_closing_element") parent typescript-ts-mode-indent-offset) + `(((match "<" "jsx_fragment") parent 0) + ((parent-is "jsx_fragment") parent typescript-ts-mode-indent-offset) ((node-is "jsx_closing_element") parent 0) - ((node-is "/") parent 0) - ((node-is ">") parent 0))) + ((node-is "jsx_element") parent typescript-ts-mode-indent-offset) + ((parent-is "jsx_element") parent typescript-ts-mode-indent-offset) + ((parent-is "jsx_opening_element") parent typescript-ts-mode-indent-offset) + ((parent-is "jsx_expression") parent-bol typescript-ts-mode-indent-offset) + ((match "/" "jsx_self_closing_element") parent 0) + ((parent-is "jsx_self_closing_element") parent typescript-ts-mode-indent-offset))) (no-node parent-bol 0)))) (defvar typescript-ts-mode--keywords diff --git a/lisp/simple.el b/lisp/simple.el index 4b09f41de55..844cfa68b08 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -4730,6 +4730,18 @@ Also see the `async-shell-command-buffer' variable." action)) (user-error "Shell command in progress")))) +(defun file-user-uid () + "Return the connection-local effective uid. +This is similar to `user-uid', but may invoke a file name handler +based on `default-directory'. See Info node `(elisp)Magic File +Names'. + +If a file name handler is unable to retrieve the effective uid, +this function will instead return -1." + (if-let ((handler (find-file-name-handler default-directory 'file-user-uid))) + (funcall handler 'file-user-uid) + (user-uid))) + (defun max-mini-window-lines (&optional frame) "Compute maximum number of lines for echo area in FRAME. As defined by `max-mini-window-height'. FRAME defaults to the diff --git a/lisp/subr.el b/lisp/subr.el index 1762c94a43e..f909b63aabe 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -2562,12 +2562,14 @@ The variable list SPEC is the same as in `if-let'." Evaluate each binding in turn, stopping if a binding value is nil. If all bindings are non-nil, eval BODY and repeat. -The variable list SPEC is the same as in `if-let'." +The variable list SPEC is the same as in `if-let*'." (declare (indent 1) (debug if-let)) (let ((done (gensym "done"))) `(catch ',done (while t - (if-let ,spec + ;; This is `if-let*', not `if-let', deliberately, despite the + ;; name of this macro. See bug#60758. + (if-let* ,spec (progn ,@body) (throw ',done nil)))))) diff --git a/lisp/treesit.el b/lisp/treesit.el index e9f5a8b37b1..69bfff21df3 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -1152,7 +1152,6 @@ See `treesit-simple-indent-presets'.") (and (>= (point) comment-start-bol) adaptive-fill-regexp (looking-at adaptive-fill-regexp) - (> (match-end 0) (match-beginning 0)) (match-end 0)))))) ;; TODO: Document. (cons 'grand-parent diff --git a/lisp/use-package/use-package-core.el b/lisp/use-package/use-package-core.el index 379e119b60f..7ab5bdc276f 100644 --- a/lisp/use-package/use-package-core.el +++ b/lisp/use-package/use-package-core.el @@ -65,7 +65,7 @@ :link '(custom-manual "(use-package) Top") :version "29.1") -(defconst use-package-version "2.4.4" +(defconst use-package-version "2.4.5" "This version of `use-package'.") (defcustom use-package-keywords diff --git a/lisp/use-package/use-package.el b/lisp/use-package/use-package.el index 7682468522d..1b63a6d651a 100644 --- a/lisp/use-package/use-package.el +++ b/lisp/use-package/use-package.el @@ -5,7 +5,7 @@ ;; Author: John Wiegley ;; Maintainer: John Wiegley ;; Created: 17 Jun 2012 -;; Version: 2.4.4 +;; Version: 2.4.5 ;; Package-Requires: ((emacs "24.3") (bind-key "2.4")) ;; Keywords: dotemacs startup speed config package extensions ;; URL: https://github.com/jwiegley/use-package diff --git a/src/w32fns.c b/src/w32fns.c index 192d3ddf27a..b4192a5ffa6 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -10396,8 +10396,8 @@ to be converted to forward slashes by the caller. */) #endif /* WINDOWSNT */ /* Query a value from the Windows Registry (under HKCU and HKLM), - where `key` is the registry key, `name` is the name, and `lpdwtype` - is a pointer to the return value's type. `lpwdtype` can be NULL if + where `key' is the registry key, `name' is the name, and `lpdwtype' + is a pointer to the return value's type. `lpwdtype' can be NULL if you do not care about the type. Returns: pointer to the value, or null pointer if the key/name does @@ -10664,7 +10664,7 @@ pops up the Windows Run dialog, - pops up the "System Properties" dialog, etc. On Windows 10, no \"Windows\" key combinations are normally handed to applications. To enable Emacs to process \"Windows\" key combinations, use the function -`w32-register-hot-key`. +`w32-register-hot-key'. For Windows 98/ME, see the doc string of `w32-phantom-key-code'. */); Vw32_pass_lwindow_to_system = Qt; @@ -10683,7 +10683,7 @@ pops up the Windows Run dialog, - pops up the "System Properties" dialog, etc. On Windows 10, no \"Windows\" key combinations are normally handed to applications. To enable Emacs to process \"Windows\" key combinations, use the function -`w32-register-hot-key`. +`w32-register-hot-key'. For Windows 98/ME, see the doc string of `w32-phantom-key-code'. */); Vw32_pass_rwindow_to_system = Qt; @@ -10698,7 +10698,7 @@ acting on \"Windows\" key events when `w32-pass-lwindow-to-system' or `w32-pass-rwindow-to-system' is nil. This variable is only used on Windows 98 and ME. For other Windows -versions, see the documentation of the `w32-register-hot-key` +versions, see the documentation of the `w32-register-hot-key' function. */); /* Although 255 is technically not a valid key code, it works and means that this hack won't interfere with any real key code. */ @@ -10732,7 +10732,7 @@ The value can be hyper, super, meta, alt, control or shift for the respective modifier, or nil to appear as the `lwindow' key. Any other value will cause the key to be ignored. -Also see the documentation of the `w32-register-hot-key` function. */); +Also see the documentation of the `w32-register-hot-key' function. */); Vw32_lwindow_modifier = Qnil; DEFVAR_LISP ("w32-rwindow-modifier", @@ -10742,7 +10742,7 @@ The value can be hyper, super, meta, alt, control or shift for the respective modifier, or nil to appear as the `rwindow' key. Any other value will cause the key to be ignored. -Also see the documentation of the `w32-register-hot-key` function. */); +Also see the documentation of the `w32-register-hot-key' function. */); Vw32_rwindow_modifier = Qnil; DEFVAR_LISP ("w32-apps-modifier", @@ -11271,7 +11271,7 @@ globals_of_w32fns (void) get_proc_addr (hm_kernel32, "SetThreadDescription"); /* Support OS dark mode on Windows 10 version 1809 and higher. - See `w32_applytheme` which uses appropriate APIs per version of Windows. + See `w32_applytheme' which uses appropriate APIs per version of Windows. For future wretches who may need to understand Windows build numbers: https://docs.microsoft.com/en-us/windows/release-health/release-information */ diff --git a/src/xterm.c b/src/xterm.c index 028bb7582c4..1373045393a 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -5750,7 +5750,8 @@ xi_device_from_id (struct x_display_info *dpyinfo, int deviceid) static void xi_link_touch_point (struct xi_device_t *device, - int detail, double x, double y) + int detail, double x, double y, + struct frame *frame) { struct xi_touch_point_t *touchpoint; @@ -5759,6 +5760,7 @@ xi_link_touch_point (struct xi_device_t *device, touchpoint->x = x; touchpoint->y = y; touchpoint->number = detail; + touchpoint->frame = frame; device->touchpoints = touchpoint; } @@ -5787,6 +5789,36 @@ xi_unlink_touch_point (int detail, return false; } +/* Unlink all touch points associated with the frame F. + This is done upon unmapping or destroying F's window, because + touch point delivery after that point is undefined. */ + +static void +xi_unlink_touch_points (struct frame *f) +{ + struct xi_device_t *device; + struct xi_touch_point_t **next, *last; + int i; + + for (i = 0; i < FRAME_DISPLAY_INFO (f)->num_devices; ++i) + { + device = &FRAME_DISPLAY_INFO (f)->devices[i]; + + /* Now unlink all touch points on DEVICE matching F. */ + + for (next = &device->touchpoints; (last = *next);) + { + if (last->frame == f) + { + *next = last->next; + xfree (last); + } + else + next = &last->next; + } + } +} + static struct xi_touch_point_t * xi_find_touch_point (struct xi_device_t *device, int detail) { @@ -13535,6 +13567,10 @@ xi_disable_devices (struct x_display_info *dpyinfo, #ifdef HAVE_XINPUT2_2 struct xi_touch_point_t *tem, *last; #endif +#if defined HAVE_XINPUT2_2 && !defined HAVE_EXT_TOOL_BAR + struct x_output *output; + Lisp_Object tail, frame; +#endif /* Don't pointlessly copy dpyinfo->devices if there are no devices to disable. */ @@ -13577,6 +13613,34 @@ xi_disable_devices (struct x_display_info *dpyinfo, tem = tem->next; xfree (last); } + +#ifndef HAVE_EXT_TOOL_BAR + + /* Now look through each frame on DPYINFO. If it has an + outstanding tool bar press for this device, release + the tool bar. */ + + FOR_EACH_FRAME (tail, frame) + { + if (!FRAME_X_P (XFRAME (frame)) + || (FRAME_DISPLAY_INFO (XFRAME (frame)) + != dpyinfo)) + continue; + + output = FRAME_OUTPUT_DATA (XFRAME (frame)); + + if (output->tool_bar_touch_device + == dpyinfo->devices[i].device_id) + { + if (XFRAME (frame)->last_tool_bar_item != -1 + && WINDOWP (XFRAME (frame)->tool_bar_window)) + handle_tool_bar_click (XFRAME (frame), 0, 0, + false, 0); + + output->tool_bar_touch_device = 0; + } + } +#endif #endif goto out; @@ -24209,6 +24273,73 @@ handle_one_xevent (struct x_display_info *dpyinfo, } #endif +#ifndef HAVE_EXT_TOOL_BAR + /* Is this a touch from a direct touch device that is in + the tool-bar? */ + if (device->direct_p + && WINDOWP (f->tool_bar_window) + && WINDOW_TOTAL_LINES (XWINDOW (f->tool_bar_window))) + { + Lisp_Object window; + int x = xev->event_x; + int y = xev->event_y; + + window = window_from_coordinates (f, x, y, 0, true, true); + /* Ignore button release events if the mouse + wasn't previously pressed on the tool bar. + We do this because otherwise selecting some + text with the mouse and then releasing it on + the tool bar doesn't stop selecting text, + since the tool bar eats the button up + event. */ + tool_bar_p = EQ (window, f->tool_bar_window); + + /* If this touch has started in the tool bar, do not + send it to Lisp. Instead, simulate a tool bar + click, releasing it once it goes away. */ + + if (tool_bar_p) + { + /* Call note_mouse_highlight on the tool bar + item. Otherwise, get_tool_bar_item will + return 1. + + This is not necessary when mouse-highlight is + nil. */ + + if (!NILP (Vmouse_highlight)) + { + note_mouse_highlight (f, x, y); + + /* Always allow future mouse motion to + update the mouse highlight, no matter + where it is. */ + memset (&dpyinfo->last_mouse_glyph, 0, + sizeof dpyinfo->last_mouse_glyph); + dpyinfo->last_mouse_glyph_frame = f; + } + + handle_tool_bar_click_with_device (f, x, y, true, 0, + (source + ? source->name : Qt)); + + /* Flush any changes made by that to the front + buffer. */ + x_flush_dirty_back_buffer_on (f); + + /* Record the device and the touch ID on the + frame. That way, Emacs knows when to dismiss + the tool bar click later. */ + + FRAME_OUTPUT_DATA (f)->tool_bar_touch_device + = device->device_id; + FRAME_OUTPUT_DATA (f)->tool_bar_touch_id = xev->detail; + + goto XI_OTHER; + } + } +#endif + if (!menu_bar_p && !tool_bar_p) { if (f && device->direct_p) @@ -24218,13 +24349,16 @@ handle_one_xevent (struct x_display_info *dpyinfo, x_catch_errors (dpyinfo->display); if (x_input_grab_touch_events) - XIAllowTouchEvents (dpyinfo->display, xev->deviceid, - xev->detail, xev->event, XIAcceptTouch); + XIAllowTouchEvents (dpyinfo->display, + xev->deviceid, + xev->detail, xev->event, + XIAcceptTouch); if (!x_had_errors_p (dpyinfo->display)) { - xi_link_touch_point (device, xev->detail, xev->event_x, - xev->event_y); + xi_link_touch_point (device, xev->detail, + xev->event_x, + xev->event_y, f); inev.ie.kind = TOUCHSCREEN_BEGIN_EVENT; inev.ie.timestamp = xev->time; @@ -24299,10 +24433,11 @@ handle_one_xevent (struct x_display_info *dpyinfo, for (touchpoint = device->touchpoints; touchpoint; touchpoint = touchpoint->next) { - arg = Fcons (list3i (lrint (touchpoint->x), - lrint (touchpoint->y), - lrint (touchpoint->number)), - arg); + if (touchpoint->frame == f) + arg = Fcons (list3i (lrint (touchpoint->x), + lrint (touchpoint->y), + lrint (touchpoint->number)), + arg); } if (source) @@ -24348,6 +24483,31 @@ handle_one_xevent (struct x_display_info *dpyinfo, } } + /* Now see if the touchpoint was previously on the tool bar. + If it was, release the tool bar. */ + + if (!f) + f = x_window_to_frame (dpyinfo, xev->event); + + if (f && (FRAME_OUTPUT_DATA (f)->tool_bar_touch_id + == xev->detail)) + { + if (f->last_tool_bar_item != -1) + handle_tool_bar_click_with_device (f, xev->event_x, + xev->event_y, + false, 0, + (source + ? source->name + : Qnil)); + + /* Cancel any outstanding mouse highlight. */ + note_mouse_highlight (f, -1, -1); + x_flush_dirty_back_buffer_on (f); + + /* Now clear the tool bar device. */ + FRAME_OUTPUT_DATA (f)->tool_bar_touch_device = 0; + } + goto XI_OTHER; } @@ -28453,6 +28613,11 @@ x_make_frame_invisible (struct frame *f) block_input (); +#ifdef HAVE_XINPUT2_2 + /* Remove any touch points associated with F. */ + xi_unlink_touch_points (f); +#endif + /* Before unmapping the window, update the WM_SIZE_HINTS property to claim that the current position of the window is user-specified, rather than program-specified, so that when the window is mapped again, it will be @@ -28658,6 +28823,11 @@ x_free_frame_resources (struct frame *f) xi_handle_delete_frame (dpyinfo, f); #endif +#ifdef HAVE_XINPUT2_2 + /* Remove any touch points associated with F. */ + xi_unlink_touch_points (f); +#endif + /* If a display connection is dead, don't try sending more commands to the X server. */ if (dpyinfo->display) diff --git a/src/xterm.h b/src/xterm.h index d768ba7ff8d..28ae00ca190 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -257,10 +257,17 @@ struct xi_scroll_valuator_t struct xi_touch_point_t { + /* The next touch point in this list. */ struct xi_touch_point_t *next; + /* The touchpoint detail. */ int number; + + /* The last known X and Y position of the touchpoint. */ double x, y; + + /* The frame associated with this touch point. */ + struct frame *frame; }; #endif @@ -1295,6 +1302,16 @@ struct x_output VisibilityFullyObscured, but is set to something else in handle_one_xevent. */ int visibility_state; + +#ifdef HAVE_XINPUT2_2 + /* The touch ID of the last touch point to have touched the tool + bar. */ + int tool_bar_touch_id; + + /* The device that last touched the tool bar. 0 if no device + touched the tool bar. */ + int tool_bar_touch_device; +#endif }; enum diff --git a/test/lisp/eshell/esh-var-tests.el b/test/lisp/eshell/esh-var-tests.el index 3f602798dbe..0cc1b92266f 100644 --- a/test/lisp/eshell/esh-var-tests.el +++ b/test/lisp/eshell/esh-var-tests.el @@ -746,6 +746,10 @@ it, since the setter is nil." (format "cd %s" ert-remote-temporary-file-directory)) (eshell-match-command-output "echo $PATH" (regexp-quote remote-path))))) +(ert-deftest esh-var-test/uid-var () + "Test that $UID is equivalent to (user-uid) for local directories." + (eshell-command-result-equal "echo $UID" (user-uid))) + (ert-deftest esh-var-test/last-status-var-lisp-command () "Test using the \"last exit status\" ($?) variable with a Lisp command" (with-temp-eshell diff --git a/test/lisp/progmodes/c-ts-mode-resources/indent.erts b/test/lisp/progmodes/c-ts-mode-resources/indent.erts index 71524e273f3..70fce68b0ec 100644 --- a/test/lisp/progmodes/c-ts-mode-resources/indent.erts +++ b/test/lisp/progmodes/c-ts-mode-resources/indent.erts @@ -133,6 +133,20 @@ Name: Multiline Block Comments 4 (bug#60270) */ =-=-= +Name: Multiline Block Comments 5 (bug#60270) + +=-= +/* +line one +line 2 + */ +=-= +/* + line one + line 2 + */ +=-=-= + Code: (lambda () diff --git a/test/lisp/progmodes/java-ts-mode-resources/indent.erts b/test/lisp/progmodes/java-ts-mode-resources/indent.erts new file mode 100644 index 00000000000..e59d5fed8e8 --- /dev/null +++ b/test/lisp/progmodes/java-ts-mode-resources/indent.erts @@ -0,0 +1,44 @@ +Code: + (lambda () + (setq indent-tabs-mode nil) + (setq java-ts-mode-indent-offset 2) + (java-ts-mode) + (indent-region (point-min) (point-max))) + +Point-Char: | + +Name: Basic + +=-= +public class Basic { + public void basic() { + return; + } +} +=-=-= + +Name: Empty Line + +=-= +public class EmptyLine { + public void emptyLine() { + | + } +} +=-=-= + +Name: Statements + +=-= +if (x) { + for (var foo : foos) { + | + } +} else if (y) { + for (int i = 0; x < foos.size(); i++) { + return; + } +} else { + return; +} +=-=-= diff --git a/test/lisp/progmodes/java-ts-mode-resources/movement.erts b/test/lisp/progmodes/java-ts-mode-resources/movement.erts new file mode 100644 index 00000000000..23639b1f5ff --- /dev/null +++ b/test/lisp/progmodes/java-ts-mode-resources/movement.erts @@ -0,0 +1,154 @@ +Code: + (lambda () + (java-ts-mode) + (forward-sentence 1)) + +Point-Char: | + +Name: forward-sentence moves over if + +=-= +public class Basic { + public void basic() { + |if (x) { + + } + log.info("some text: {}", text); + return; + } +} +=-= +public class Basic { + public void basic() { + if (x) { + + }| + log.info("some text: {}", text); + return; + } +} +=-=-= + +Name: forward-sentence moves over method invocation + +=-= +public class Basic { + public void basic() { + |log.info("some text: {}", text); + } +} +=-= +public class Basic { + public void basic() { + log.info("some text: {}", text);| + } +} +=-=-= + +Code: + (lambda () + (java-ts-mode) + (forward-sentence 2)) + +Name: forward-sentence moves over multiple statements + +=-= +public class Basic { + public void basic() { + |return; + return; + } +} +=-= +public class Basic { + public void basic() { + return; + return;| + } +} +=-=-= + +Code: + (lambda () + (java-ts-mode) + (backward-sentence 1)) + +Name: backward-sentence moves over one statement + +=-= +public class Basic { + public void basic() { + return;| + } +} +=-= +public class Basic { + public void basic() { + |return; + } +} +=-=-= + +Code: + (lambda () + (java-ts-mode) + (beginning-of-defun)) + +Name: beginning-of-defun moves to defun start + +=-= +public class Basic { + public void basic() { + return;| + } +} +=-= +public class Basic { +| public void basic() { + return; + } +} +=-=-= + +Code: + (lambda () + (java-ts-mode) + (beginning-of-defun) + (beginning-of-defun)) + +Name: beginning-of-defun moves to class + +=-= +public class Basic { + public void basic() { + return;| + } +} +=-= +|public class Basic { + public void basic() { + return; + } +} +=-=-= + +Code: + (lambda () + (java-ts-mode) + (end-of-defun)) + +Name: end-of-defun moves to defun end + +=-= +public class Basic { + public void basic() { + return;| + } +} +=-= +public class Basic { + public void basic() { + return; + } +|} +=-=-= diff --git a/test/lisp/progmodes/java-ts-mode-tests.el b/test/lisp/progmodes/java-ts-mode-tests.el new file mode 100644 index 00000000000..4fd8fc3019f --- /dev/null +++ b/test/lisp/progmodes/java-ts-mode-tests.el @@ -0,0 +1,35 @@ +;;; java-ts-mode-tests.el --- Tests for Tree-sitter-based Java mode -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Code: + +(require 'ert) +(require 'ert-x) +(require 'treesit) + +(ert-deftest java-ts-mode-test-indentation () + (skip-unless (treesit-ready-p 'java)) + (ert-test-erts-file (ert-resource-file "indent.erts"))) + +(ert-deftest java-ts-mode-test-movement () + (skip-unless (treesit-ready-p 'java)) + (ert-test-erts-file (ert-resource-file "movement.erts"))) + +(provide 'java-ts-mode-tests) +;;; java-ts-mode-tests.el ends here diff --git a/test/lisp/progmodes/ruby-ts-mode-tests.el b/test/lisp/progmodes/ruby-ts-mode-tests.el index b2c990f8e56..eaf6367a306 100644 --- a/test/lisp/progmodes/ruby-ts-mode-tests.el +++ b/test/lisp/progmodes/ruby-ts-mode-tests.el @@ -251,6 +251,7 @@ The whitespace before and including \"|\" on each line is removed." (kill-buffer buf))))) (ruby-ts-deftest-indent "ruby-method-params-indent.rb") +(ruby-ts-deftest-indent "ruby-block-indent.rb") (provide 'ruby-ts-mode-tests) diff --git a/test/lisp/progmodes/typescript-ts-mode-resources/indent.erts b/test/lisp/progmodes/typescript-ts-mode-resources/indent.erts new file mode 100644 index 00000000000..146ee76574e --- /dev/null +++ b/test/lisp/progmodes/typescript-ts-mode-resources/indent.erts @@ -0,0 +1,73 @@ +Code: + (lambda () + (setq indent-tabs-mode nil) + (setq typescript-ts-mode-indent-offset 2) + (typescript-ts-mode) + (indent-region (point-min) (point-max))) + +Point-Char: | + +Name: Basic indentation + +=-= +const foo = () => { + console.log("bar"); + if (x) { + return y; + } else if (y) { + return u; + } + return baz.x() + ? true + : false; +} +=-=-= + +Code: + (lambda () + (setq indent-tabs-mode nil) + (setq tsx-ts-mode-indent-offset 2) + (tsx-ts-mode) + (indent-region (point-min) (point-max))) + +Name: JSX indentation + +=-= +const foo = (props) => { + return ( +
+
+
+
+ { + props.foo + ? Hello, foo! + : Hello, World!; + } +
+
+
+
+ ); +} +=-=-= + +Name: JSX indentation with attributes + +=-= +const foo = (props) => { + return ( +
{ + alert('???'); + return () => { + return 5+5; + }; + }} + > +

Some text

+
+ ); +} +=-=-= diff --git a/test/lisp/progmodes/typescript-ts-mode-tests.el b/test/lisp/progmodes/typescript-ts-mode-tests.el new file mode 100644 index 00000000000..126f5e3298f --- /dev/null +++ b/test/lisp/progmodes/typescript-ts-mode-tests.el @@ -0,0 +1,31 @@ +;;; typescript-ts-mode-tests.el --- Tests for Tree-sitter-based TypeScript mode -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Code: + +(require 'ert) +(require 'ert-x) +(require 'treesit) + +(ert-deftest typescript-ts-mode-test-indentation () + (skip-unless (treesit-ready-p 'typescript)) + (ert-test-erts-file (ert-resource-file "indent.erts"))) + +(provide 'typescript-ts-mode-tests) +;;; typescript-ts-mode-tests.el ends here