commit a542ed23e488675723832071e2ca2fc7333beed7 (HEAD, refs/remotes/origin/master) Author: João Távora Date: Wed Nov 19 11:14:43 2025 +0000 Eglot: re-do semantic tokens again (bug#79374) After a week of intense testing, found out there were various insidious bugs related to out-of-date requests that rear their heads in different typing patterns. One way to sort this out and still minimize the number of requests is to have Eglot's semtok code be aware of the states a request for tokens can be in: inexistent/invalid, unsent, sent, valid. The needs and optimization opportunities for servers that support "full" and "full/delta" requests are considerably different from the ones of servers which only support "full" and "range". The latter seem to be in the minority, so for now I've removed the range requests. These servers should still work with "full" requests. In a further bug, at least two servers (clangd and lean) were found to plainly abuse the semanticTokens/refresh server request. This confuses the state logic. The solution is to simply no-op this request handler, it doesn't seem to do anything useful. Finally, when auto-reverting with preserve-modes (as done by vc-revert and auto-revert-mode), the font-lock state needs to be reinitialized. Fixed this in eglot--after-revert-hook. * lisp/progmodes/eglot.el (eglot-client-capabilities): Don't announce ranged request support. (eglot--docver): Rename from eglot--versioned-identifier. (eglot--diagnostics) (eglot--flymake-diagnostics) (eglot-handle-notification) (eglot--signal-textDocument/didOpen) (eglot--report-to-flymake) (eglot--apply-text-edits) (eglot--VersionedTextDocumentIdentifier): Use eglot--docver. (eglot--send-changes-hook): Rename from eglot--document-changed-hook. (eglot--after-change): Don't run eglot--send-changes-hook here. (eglot--signal-textDocument/didChange): Run it here. (eglot--semtok-state): Rename from eglot--semtok-cache. (eglot--semtok-inflight): Delete. (eglot--handle-request semanticTokens/refresh): Nullify. (eglot-semantic-tokens-mode): Tweak. (eglot--semtok-after-send-changes): New hook. (eglot--semtok-request) (eglot--semtok-font-lock): Rewrite. (eglot--after-revert-hook): Reinitialize semtok state. Co-authored-by: Lua Viana Reis diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index e2c76656968..9a6da5dc804 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -1125,7 +1125,7 @@ object." :rangeFormatting `(:dynamicRegistration :json-false) :rename `(:dynamicRegistration :json-false) :semanticTokens `(:dynamicRegistration :json-false - :requests '(:range t :full (:delta t)) + :requests '(:full (:delta t)) :overlappingTokenSupport t :multilineTokenSupport t :tokenTypes [,@eglot-semantic-token-types] @@ -1304,7 +1304,7 @@ If optional MARKERS, make markers instead." (cl-defmethod initialize-instance :before ((_server eglot-lsp-server) &optional args) (cl-remf args :initializationOptions)) -(defvar-local eglot--versioned-identifier 0 +(defvar-local eglot--docver 0 "LSP document version. Bumped on `eglot--after-change'.") (defvar eglot--servers-by-project (make-hash-table :test #'equal) @@ -2337,12 +2337,16 @@ Use `eglot-managed-p' to determine if current buffer is managed.") "A cons (DIAGNOSTICS . VERSION) for current buffer. DIAGNOSTICS is a list of Flymake diagnostics objects. VERSION is the LSP Document version reported for DIAGNOSTICS (comparable to -`eglot--versioned-identifier') or nil if server didn't bother.") +`eglot--docver') or nil if server didn't bother.") (defvar revert-buffer-preserve-modes) +(defvar eglot-semantic-tokens-mode) ;; forward decl (defun eglot--after-revert-hook () "Eglot's `after-revert-hook'." - (when revert-buffer-preserve-modes (eglot--signal-textDocument/didOpen))) + (when revert-buffer-preserve-modes + (eglot--signal-textDocument/didOpen) + (when eglot-semantic-tokens-mode + (eglot-semantic-tokens-mode)))) (defun eglot--maybe-activate-editing-mode () "Maybe activate `eglot--managed-mode'. @@ -2639,7 +2643,7 @@ still unanswered LSP requests to the server\n")))) for lsp-diag = (alist-get 'eglot-lsp-diag data) for version = (alist-get 'eglot--doc-version data) when (and lsp-diag (or (null version) - (= version eglot--versioned-identifier))) + (= version eglot--docver))) collect diag)) (defun eglot--diag-to-lsp-diag (diag) @@ -2770,11 +2774,11 @@ expensive cached value of `file-truename'.") (with-current-buffer buffer (cl-loop initially - (if (and version (/= version eglot--versioned-identifier)) + (if (and version (/= version eglot--docver)) (cl-return)) (setq ;; if no explicit version received, assume it's current. - version eglot--versioned-identifier + version eglot--docver flymake-list-only-diagnostics (assoc-delete-all path flymake-list-only-diagnostics)) for diag-spec across diagnostics @@ -2907,7 +2911,7 @@ Sets `eglot--TextDocumentIdentifier-cache' (which see) as a side effect." (defun eglot--VersionedTextDocumentIdentifier () "Compute VersionedTextDocumentIdentifier object for current buffer." (append (eglot--TextDocumentIdentifier) - `(:version ,eglot--versioned-identifier))) + `(:version ,eglot--docver))) (cl-defun eglot--languageId (&optional (server (eglot--current-server-or-lose))) "Compute LSP \\='languageId\\=' string for current buffer. @@ -2992,13 +2996,13 @@ buffer." (,end . ,(copy-marker end t))) eglot--recent-changes))) -(defvar eglot--document-changed-hook '(eglot--signal-textDocument/didChange) +(defvar eglot--send-changes-hook '() "Internal hook for doing things when the document changes.") (defun eglot--after-change (beg end pre-change-length) "Hook onto `after-change-functions'. Records BEG, END and PRE-CHANGE-LENGTH locally." - (cl-incf eglot--versioned-identifier) + (cl-incf eglot--docver) (pcase (car-safe eglot--recent-changes) (`(,lsp-beg ,lsp-end (,b-beg . ,b-beg-marker) @@ -3040,7 +3044,7 @@ Records BEG, END and PRE-CHANGE-LENGTH locally." eglot-send-changes-idle-time nil (lambda () (eglot--when-live-buffer buf (when eglot--managed-mode - (run-hooks 'eglot--document-changed-hook) + (eglot--signal-textDocument/didChange) (setq eglot--change-idle-timer nil)))))))) (defvar-local eglot-workspace-configuration () @@ -3165,13 +3169,14 @@ When called interactively, use the currently active server" vconcat `[,(list :range `(:start ,beg :end ,end) :rangeLength len :text text)])))) (setq eglot--recent-changes nil) - (jsonrpc--call-deferred server)))) + (jsonrpc--call-deferred server) + (run-hooks 'eglot--send-changes-hook)))) (defun eglot--signal-textDocument/didOpen () "Send textDocument/didOpen to server." ;; Flush any potential pending change. (setq eglot--recent-changes nil - eglot--versioned-identifier 0 + eglot--docver 0 eglot--TextDocumentIdentifier-cache nil) (jsonrpc-notify (eglot--current-server-or-lose) @@ -3226,7 +3231,7 @@ may be called multiple times (respecting the protocol of "Internal helper for `eglot-flymake-backend'." (save-restriction (widen) - (if (or (null version) (= version eglot--versioned-identifier)) + (if (or (null version) (= version eglot--docver)) (funcall eglot--current-flymake-report-fn diags ;; If the buffer hasn't changed since last ;; call to the report function, flymake won't @@ -3938,9 +3943,9 @@ Returns a list as described in docstring of `imenu--index-alist'." "Apply EDITS for current buffer if at VERSION, or if it's nil. If SILENT, don't echo progress in mode-line." (unless edits (cl-return-from eglot--apply-text-edits)) - (unless (or (not version) (equal version eglot--versioned-identifier)) + (unless (or (not version) (equal version eglot--docver)) (jsonrpc-error "Edits on `%s' require version %d, you have %d" - (current-buffer) version eglot--versioned-identifier)) + (current-buffer) version eglot--docver)) (atomic-change-group (let* ((change-group (prepare-change-group)) (howmany (length edits)) @@ -4618,34 +4623,36 @@ If NOERROR, return predicate, else erroring function." semtok-cache) probe)))) -(defvar-local eglot--semtok-cache nil - "List of plists describing recent semtok response. +(defvar-local eglot--semtok-state nil + "Plist describing current semtok state. See `eglot--semtok-request' implementation for details.") -(defvar-local eglot--semtok-inflight (make-hash-table) - "Map of JSONRPC request ID to (METHOD DOCVER . REGIONS). -REGIONS is a list of (BEG . END) of positions that can be serviced by -this request.") - (cl-defmethod eglot-handle-request (server (_method (eql workspace/semanticTokens/refresh))) "Handle a semanticTokens/refresh request from SERVER." (dolist (buffer (eglot--managed-buffers server)) (eglot--when-live-buffer buffer - (eglot--widening - (font-lock-flush))))) + ;; JT@2025-11-20: As of time of writing, at least two servers (clangd and lean) + ;; abuse this entry point. There is no practical benefit to having it enabled, + ;; just disadvantages. + ;; (unless (zerop eglot--docver) + ;; (eglot--widening (font-lock-flush))) + ))) (define-minor-mode eglot-semantic-tokens-mode "Minor mode for fontifying buffer with LSP server's semantic tokens." :global nil - (setq eglot--semtok-cache nil) - (clrhash eglot--semtok-inflight) + (setq eglot--semtok-state nil) (cond (eglot-semantic-tokens-mode (if (not (eglot-server-capable :semanticTokensProvider)) (eglot-semantic-tokens-mode -1) + (add-hook 'eglot--send-changes-hook + #'eglot--semtok-after-send-changes) (font-lock-add-keywords nil '((eglot--semtok-font-lock)) 'append) (font-lock-flush))) (t + (remove-hook 'eglot--send-changes-hook + #'eglot--semtok-after-send-changes) (font-lock-remove-keywords nil '((eglot--semtok-font-lock))) (font-lock-flush)))) @@ -4660,116 +4667,73 @@ this request.") finally (cl-return (vconcat new (substring old-data old-i (length old-data)))))) -(cl-defun eglot--semtok-request - (beg end &aux (docver eglot--versioned-identifier) reused) +(defun eglot--semtok-after-send-changes () + ;; (trace-values "Dispatching") + (setf (plist-get eglot--semtok-state :dispatched) t)) + +(cl-defun eglot--semtok-request (beg end &aux (docver eglot--docver)) "Ask for tokens. Arrange for BEG..END to be font-lock flushed." - (cl-labels - ((fullish-p (m) - (memq m '(:textDocument/semanticTokens/full/delta - :textDocument/semanticTokens/full))) - (prune-outdated () - (setq eglot--semtok-cache - (cl-delete-if (lambda (e) - (not (eq docver (plist-get e :docver)))) - eglot--semtok-cache))) - (req (method params &optional cont - &aux req-id (buf (current-buffer))) - (setq req-id - (eglot--async-request - (eglot--current-server-or-lose) method params - :success-fn - (lambda (response) - (eglot--when-live-buffer buf - (pcase-let ((`(,method ,docver ,regions) - (gethash req-id eglot--semtok-inflight))) - (remhash req-id eglot--semtok-inflight) - ;; (trace-values "Response: " - ;; method - ;; eglot--versioned-identifier docver - ;; "edits: " - ;; (length (cl-getf response :edits)) - ;; "data: " - ;; (length (cl-getf response :data))) - ;; A user edit may have come in while the request - ;; was inflight, changing the state of the buffer... - (when (eq docver eglot--versioned-identifier) - (push - (list :docver docver - :method method - :resultId (plist-get response :resultId) - :data (if cont (funcall cont response) - (plist-get response :data)) - :valid (if (fullish-p method) - (eglot--widening - (cons (point-min) (point-max))) - (cons beg end))) - eglot--semtok-cache)) - ;; ... but we should flush unconditionally. If - ;; this response was out-of-date, - ;; `eglot--semtok-font-lock' should just trigger - ;; another request. - (cl-loop for (b . e) in regions - do (font-lock-flush b e)) - ;; (trace-values "Flushed" (length regions) - ;; "regions" regions) - ))) - ;; For "range" requests, make sure we have one unique - ;; request defeating part of the "deferred" mechanism. - :hint (if (fullish-p method) method - (gensym (symbol-name method))))) - ;; Can prune outdated entries now, not earlier, since "delta" - ;; requests rely on outdated entries by definition. - (prune-outdated) - (puthash req-id (list method docver (list (cons beg end))) - eglot--semtok-inflight))) - ;; JT@2025-11-16: Many back-to-back calls for - ;; `eglot--semtok-request' and small regions occur even on - ;; trivial/fast edits. We try to send just one request. If there - ;; is a "full" or "full/delta" request in flight, we can piggy back - ;; onto it our region and our docver, and exit. That's because very - ;; likely it's not actually inflight yet (because of the "deferred" - ;; mechanism, it's waiting for didChange), so we can still do - ;; changes to the state it represents when it is actually sent. - (cl-loop for v being the hash-values of eglot--semtok-inflight - when (fullish-p (car v)) do - (push (cons beg end) (caddr v)) - (setf (cadr v) docver) - (cl-return-from eglot--semtok-request (cons 'skipped docver))) - (cond - ((and (eglot-server-capable :semanticTokensProvider :full :delta) - (setq reused (cl-find-if - (lambda (e) (fullish-p (plist-get e :method))) - eglot--semtok-cache))) - (req :textDocument/semanticTokens/full/delta - (list :textDocument (eglot--TextDocumentIdentifier) - :previousResultId (plist-get reused :resultId)) - (lambda (response) - (if-let* ((edits (plist-get response :edits))) - (eglot--semtok-apply-delta-edits - (plist-get reused :data) - edits) - (plist-get response :data))))) - ((eglot-server-capable :semanticTokensProvider :range) - (req :textDocument/semanticTokens/range - (list :textDocument (eglot--TextDocumentIdentifier) - :range (eglot-region-range beg end)))) - (t - (req :textDocument/semanticTokens/full - (list :textDocument (eglot--TextDocumentIdentifier))))))) + (cl-macrolet ((c (tag) `(plist-get eglot--semtok-state ,tag))) + (cl-labels + ((req (method &optional params cont + &aux (buf (current-buffer))) + (setf (c :req-docver) docver + (c :orig-docver) docver + (c :dispatched) (not eglot--recent-changes) + (c :regions) (cons (cons (copy-marker beg) (copy-marker end)) (c :regions))) + ;; (trace-values "Request" method) + (eglot--async-request + (eglot--current-server-or-lose) method + (append (nconc params `(:textDocument ,(eglot--TextDocumentIdentifier)))) + :success-fn + (lambda (response) + (eglot--when-live-buffer buf + ;; (trace-values "Response" + ;; eglot--docver docver (c :orig-docver) (c :req-docver)) + ;; This skip is different from the one below. Comparing + ;; the lexical `docver' to the original request's + ;; `:orig-docver' allows skipping the outdated reponse + ;; of a dispatched request that has been overriden by + ;; another (perhaps not dispatched yet) request. + (when (eq docver (c :orig-docver)) + (setf (c :docver) (c :req-docver) + (c :data) (if cont (funcall cont response) + (plist-get response :data)) + (c :resultId) (plist-get response :resultId)) + ;; (trace-values "Flushing" (length (c :regions)) "regions") + (cl-loop for (a . b) in (c :regions) do (font-lock-flush a b)) + (setf (c :regions) nil)))) + :hint 'semtok))) + ;; Skip actually making the request if there's an undispatched + ;; waiting for a eglot--send-changes-hook flush. Just update the + ;; regions and the `:req-docver'. + (unless (or (null (c :req-docver)) (c :dispatched)) + (push (cons (copy-marker beg) (copy-marker end)) (c :regions)) + (setf (c :req-docver) eglot--docver) + (cl-return-from eglot--semtok-request 'skipped)) + (cond + ((and (eglot-server-capable :semanticTokensProvider :full :delta) + (c :data)) + (req :textDocument/semanticTokens/full/delta + `(:previousResultId ,(c :resultId)) + (let ((data (c :data) )) + (lambda (response) + (if-let* ((edits (plist-get response :edits))) + (eglot--semtok-apply-delta-edits data edits) + (plist-get response :data)))))) + (t + (req :textDocument/semanticTokens/full)))))) (cl-defun eglot--semtok-font-lock (limit &aux (beg (point)) (end limit)) "Arrange for font-lock to happen from point until LIMIT. Either do it immediately if the information available is up-to-date or request new information from the server and return and hope the font lock machinery calls us again." - (let ((probe - (cl-find-if - (jsonrpc-lambda (&key docver valid &allow-other-keys) - (and (eq docver eglot--versioned-identifier) - (<= (car valid) beg end (cdr valid)))) - eglot--semtok-cache))) - (cond (probe - (eglot--semtok-font-lock-1 beg end (plist-get probe :data))) + (cl-macrolet ((c (tag) `(plist-get eglot--semtok-state ,tag))) + (cond ((and (eq (c :docver) eglot--docver) + (c :dispatched) + (c :data)) + (eglot--semtok-font-lock-1 beg end (c :data))) (t (eglot--semtok-font-lock-2 beg end) (eglot--semtok-request beg end)))) commit 80a84130a4191a2bdbe2a255ce06b114d9be366e Author: João Távora Date: Thu Nov 20 13:24:58 2025 +0000 ; Eglot: remove/address a FIXME added by Stefan Monnier According to the commit msg of: commit 0816da8e78f1d0d643fdd1259a2f307c0817e316 Author: João Távora Date: Mon Oct 21 16:07:38 2019 +0100 This is needed because M-x vc-revert preserves (or used to preserve) major modes on revert. * lisp/progmodes/eglot.el (eglot--after-revert-hook): Remove FIXME comment. diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index f46d6b2ee76..e2c76656968 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -2342,7 +2342,6 @@ LSP Document version reported for DIAGNOSTICS (comparable to (defvar revert-buffer-preserve-modes) (defun eglot--after-revert-hook () "Eglot's `after-revert-hook'." - ;; FIXME: Do we really need this? (when revert-buffer-preserve-modes (eglot--signal-textDocument/didOpen))) (defun eglot--maybe-activate-editing-mode () commit 6346f5b053b80053c65822c06ddd5812a2428391 Author: Michael Albinus Date: Fri Nov 28 17:38:36 2025 +0100 Sync with Tramp 2.8.0.5 * doc/misc/tramp.texi (Remote processes): Improve description where direct asyn processes won't work. * lisp/net/tramp-cache.el (with-tramp-saved-connection-properties): Adapt `tramp-verbose' less aggressive. * lisp/net/tramp-container.el (tramp-kubernetes--context-namespace): Use `string-join'. * lisp/net/tramp-message.el (tramp-get-debug-file-name): Modify debug file name. (tramp-debug-message): Simplify. * lisp/net/tramp-sh.el (tramp-actions-before-shell) (tramp-actions-copy-out-of-band): Add `tramp-keyboard-interactive-authentication-prompt-regexp'. * lisp/net/tramp.el (tramp-encoding-shell): Fix docstring. (tramp-keyboard-interactive-authentication-prompt-regexp): New defcustom. (tramp-action-ignore-message): New defun. * test/lisp/net/tramp-tests.el (tramp-test10-write-region) (tramp-test10-write-region-file-precious-flag) (tramp-test10-write-region-other-file-name-handler) (tramp-test27-load, tramp-test32-shell-command) (tramp-test32-shell-command-dont-erase-buffer) (tramp-test34-connection-local-variables) (tramp-test36-vc-registered, tramp-test39-make-lock-file-name) (tramp-test39-detect-external-change) (tramp-test45-asynchronous-requests): Adapt tests. diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi index 77f73196383..424bd8436e4 100644 --- a/doc/misc/tramp.texi +++ b/doc/misc/tramp.texi @@ -4737,7 +4737,9 @@ It works only for some connection methods defined in It does not support interactive user authentication. With @option{ssh}-based methods, this can be avoided by using a password agent like @command{ssh-agent}, using public key authentication, or -using @option{ControlMaster} options. +using @option{ControlMaster} options.@* +Other interactive authentication requests, like one-time passwords or +security key confirmation messages, do not work either. @item It cannot be applied for @option{ssh}-based methods, which use the diff --git a/lisp/net/tramp-cache.el b/lisp/net/tramp-cache.el index 941c502fa83..36fc8c3953b 100644 --- a/lisp/net/tramp-cache.el +++ b/lisp/net/tramp-cache.el @@ -512,7 +512,9 @@ PROPERTIES is a list of file properties (strings)." (cons property (gethash property hash tramp-cache-undefined))) ,properties)) ;; Avoid superfluous debug buffers during host name completion. - (tramp-verbose (if minibuffer-completing-file-name 0 tramp-verbose))) + (tramp-verbose + (if minibuffer-completing-file-name + (min 6 tramp-verbose) tramp-verbose))) (tramp-message key 7 "Saved %s" values) (unwind-protect (progn ,@body) ;; Reset PROPERTIES. Recompute hash, it could have been flushed. diff --git a/lisp/net/tramp-container.el b/lisp/net/tramp-container.el index 5ac2e46819a..897a4dcc72c 100644 --- a/lisp/net/tramp-container.el +++ b/lisp/net/tramp-container.el @@ -427,8 +427,7 @@ Obey `tramp-kubernetes-context'" ;;;###tramp-autoload (defun tramp-kubernetes--context-namespace (vec) "The kubectl options for context and namespace as string." - (mapconcat - #'identity + (string-join (delq nil `(,(when-let* ((context (tramp-kubernetes--current-context vec))) (format "--context=%s" context)) diff --git a/lisp/net/tramp-message.el b/lisp/net/tramp-message.el index aafceaf3335..eecddc978cc 100644 --- a/lisp/net/tramp-message.el +++ b/lisp/net/tramp-message.el @@ -194,11 +194,13 @@ They are completed by `M-x TAB' only in Tramp debug buffers." (tramp-setup-debug-buffer)) (current-buffer))) +;; On some systems, file names starting with "*" do not work. (defun tramp-get-debug-file-name (vec) "Get the debug file name for VEC." (declare (tramp-suppress-trace t)) (expand-file-name - (string-replace "/" " " (tramp-debug-buffer-name vec)) + (string-replace + "/" " " (substring (tramp-debug-buffer-name vec) 1 -1)) tramp-compat-temporary-file-directory)) (defun tramp-trace-buffer-name (vec) @@ -227,13 +229,12 @@ ARGUMENTS to actually emit the message (if applicable)." ";; Emacs: %s Tramp: %s -*- mode: outline; coding: utf-8; -*-" emacs-version tramp-version)) (when (>= tramp-verbose 10) - (let ((tramp-verbose 0)) - (insert - (format - "\n;; Location: %s Git: %s/%s" - (locate-library "tramp") - (or tramp-repository-branch "") - (or tramp-repository-version ""))))) + (insert + (format + "\n;; Location: %s Git: %s/%s" + (locate-library "tramp") + (or tramp-repository-branch "") + (or tramp-repository-version "")))) ;; Traces. (when (>= tramp-verbose 11) (dolist @@ -249,7 +250,7 @@ ARGUMENTS to actually emit the message (if applicable)." (ignore-errors (delete-file (tramp-get-debug-file-name vec))) (let ((message-log-max t)) (message - "Tramp debug file is %s" (tramp-get-debug-file-name vec))))) + "Tramp debug file is \"%s\"" (tramp-get-debug-file-name vec))))) (unless (bolp) (insert "\n")) ;; Timestamp. diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index e55af1f9659..5ed3e91da24 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -649,7 +649,9 @@ shell from reading its init file." ;;;###tramp-autoload (defconst tramp-actions-before-shell - '((tramp-login-prompt-regexp tramp-action-login) + '((tramp-keyboard-interactive-authentication-prompt-regexp + tramp-action-ignore-message) + (tramp-login-prompt-regexp tramp-action-login) (tramp-password-prompt-regexp tramp-action-password) (tramp-otp-password-prompt-regexp tramp-action-otp-password) (tramp-fingerprint-prompt-regexp tramp-action-fingerprint) @@ -676,7 +678,9 @@ The ACTION should also be a symbol, but a function. When the corresponding PATTERN matches, the ACTION function is called.") (defconst tramp-actions-copy-out-of-band - '((tramp-password-prompt-regexp tramp-action-password) + '((tramp-keyboard-interactive-authentication-prompt-regexp + tramp-action-ignore-message) + (tramp-password-prompt-regexp tramp-action-password) (tramp-otp-password-prompt-regexp tramp-action-otp-password) (tramp-wrong-passwd-regexp tramp-action-permission-denied) (tramp-copy-failed-regexp tramp-action-permission-denied) diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index be13afebeba..5edb2c703b8 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -36,8 +36,8 @@ ;; This package provides remote file editing, similar to ange-ftp. ;; The difference is that ange-ftp uses FTP to transfer files between -;; the local and the remote host, whereas tramp.el uses a combination -;; of rsh and rcp or other work-alike programs, such as ssh/scp. +;; the local and the remote host, whereas Tramp uses a combination of +;; rsh and rcp or other work-alike programs, such as ssh/scp. ;; ;; For more detailed instructions, please see the info file. ;; @@ -190,8 +190,8 @@ If the shell must be forced to be interactive, see `tramp-encoding-command-interactive'. Note that this variable is not used for remote commands. There are -mechanisms in tramp.el which automatically determine the right shell to -use for the remote host." +mechanisms in Tramp which automatically determine the right shell to use +for the remote host." :type '(file :must-match t) :link '(info-link :tag "Tramp manual" "(tramp) Remote shell setup")) @@ -661,6 +661,17 @@ I don't think this ever needs to be changed, so please tell me about it if you need to change this." :type 'string) +(defcustom tramp-keyboard-interactive-authentication-prompt-regexp + ;; This shouldn't be needed. But sometimes, this message happens + ;; after the first request, although it shall be prior any + ;; authentication request. + (rx "-- Keyboard-interactive authentication prompts " + "from server: ------------------" (* blank) (* (any "\r\n"))) + "Regexp matching keyboard-interactive authentication requests. +The regexp should match at end of buffer." + :version "31.1" + :type 'regexp) + (defcustom tramp-login-prompt-regexp (rx (* nonl) (| "user" "login") (? blank (* nonl)) ":" (* blank)) "Regexp matching login-like prompts. @@ -714,7 +725,7 @@ The `sudo' program appears to insert a `^@' character into the prompt." (rx-to-string `(: bol (* nonl) (group (| - ;; JumpCloud. + ;; JumpCloud. Google Authenticator. "Verification code" ;; TACC HPC. "TACC Token Code")) @@ -5940,6 +5951,17 @@ of." (defvar tramp-process-action-regexp nil "The regexp used to invoke an action in `tramp-process-one-action'.") +(defun tramp-action-ignore-message (proc vec) + "Ignore the message. +Keep the rest for next check." + (with-current-buffer (process-buffer proc) + (tramp-message vec 6 "\n%s" (buffer-string)) + (goto-char (point-min)) + (tramp-check-for-regexp proc tramp-process-action-regexp) + (replace-match "") + (tramp-message vec 10 "\n%s" (buffer-string))) + nil) + (defun tramp-action-login (_proc vec) "Send the login name." (let ((user (or (tramp-file-name-user vec) diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index 7099a614a40..76e9a47e41b 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -2675,7 +2675,7 @@ This checks also `file-name-as-directory', `file-name-directory', (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil))) (let ((tmp-name (tramp--test-make-temp-name nil quoted)) - (inhibit-message t)) + (inhibit-message (not (ignore-errors (edebug-mode))))) (unwind-protect (progn ;; Write buffer. Use absolute and relative file name. @@ -2801,7 +2801,7 @@ This checks also `file-name-as-directory', `file-name-directory', (skip-unless (tramp--test-sh-p)) (let* ((tmp-name (tramp--test-make-temp-name)) - (inhibit-message t) + (inhibit-message (not (ignore-errors (edebug-mode)))) written-files (advice (lambda (_start _end filename &rest _r) (push filename written-files)))) @@ -2838,7 +2838,7 @@ This checks also `file-name-as-directory', `file-name-directory', (archive (ert-resource-file "foo.tar.gz")) (tmp-file (expand-file-name (file-name-nondirectory archive))) (require-final-newline t) - (inhibit-message t) + (inhibit-message (not (ignore-errors (edebug-mode)))) (backup-inhibited t) create-lockfiles buffer1 buffer2) (unwind-protect @@ -5296,7 +5296,9 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (unless (tramp--test-ange-ftp-p) (load tmp-name 'noerror 'nomessage)) (should-not (featurep 'tramp-test-load)) - (write-region "(provide 'tramp-test-load)" nil tmp-name) + (write-region + ";;; -*- lexical-binding: t; -*-\n(provide 'tramp-test-load)" + nil tmp-name) ;; `load' in lread.c passes `must-suffix' since Emacs 29. ;; In Ange-FTP, `must-suffix' is ignored. (when (and (tramp--test-emacs29-p) @@ -6119,7 +6121,7 @@ INPUT, if non-nil, is a string sent to the process." (let ((tmp-name (tramp--test-make-temp-name nil quoted)) (default-directory ert-remote-temporary-file-directory) ;; Suppress nasty messages. - (inhibit-message t) + (inhibit-message (not (ignore-errors (edebug-mode)))) kill-buffer-query-functions) (dolist (this-shell-command @@ -6237,7 +6239,7 @@ INPUT, if non-nil, is a string sent to the process." ;; ----------------------------------------------- (let (;; Suppress nasty messages. - (inhibit-message t) + (inhibit-message (not (ignore-errors (edebug-mode)))) buffer kill-buffer-query-functions) ;; We check both the local and remote case, in order to guarantee ;; that they behave similar. @@ -6468,7 +6470,7 @@ INPUT, if non-nil, is a string sent to the process." (tmp-name2 (expand-file-name "foo" tmp-name1)) (enable-local-variables :all) (enable-remote-dir-locals t) - (inhibit-message t) + (inhibit-message (not (ignore-errors (edebug-mode)))) kill-buffer-query-functions (clpa connection-local-profile-alist) (clca connection-local-criteria-alist)) @@ -6735,7 +6737,7 @@ INPUT, if non-nil, is a string sent to the process." (tmp-name2 (expand-file-name "foo" tmp-name1)) (tramp-remote-process-environment tramp-remote-process-environment) ;; Suppress nasty messages. - (inhibit-message t) + (inhibit-message (not (ignore-errors (edebug-mode)))) (vc-handled-backends (cond ((tramp-find-executable @@ -7069,7 +7071,7 @@ INPUT, if non-nil, is a string sent to the process." (remote-file-name-inhibit-locks nil) (create-lockfiles t) tramp-allow-unsafe-temporary-files - (inhibit-message t) + (inhibit-message (not (ignore-errors (edebug-mode)))) ;; tramp-rclone.el and tramp-sshfs.el cache the mounted files. (tramp-fuse-unmount-on-cleanup t) auto-save-default @@ -7229,7 +7231,7 @@ INPUT, if non-nil, is a string sent to the process." (remote-file-name-inhibit-cache t) (remote-file-name-inhibit-locks nil) tramp-allow-unsafe-temporary-files - (inhibit-message t) + (inhibit-message (not (ignore-errors (edebug-mode)))) ;; tramp-rclone.el and tramp-sshfs.el cache the mounted files. (tramp-fuse-unmount-on-cleanup t) auto-save-default @@ -8005,7 +8007,7 @@ process sentinels. They shall not disturb each other." (remote-file-name-inhibit-cache t) (process-file-side-effects t) ;; Suppress nasty messages. - (inhibit-message t) + (inhibit-message (not (ignore-errors (edebug-mode)))) ;; Do not run delayed timers. (timer-max-repeats 0) ;; Number of asynchronous processes for test. Tests on commit 2c19f6087efe466894e1fe47121515d32d6a6d1c Author: Yuan Fu Date: Fri Nov 28 00:10:17 2025 -0800 Remove the cached values in tree-sitter modes (bug#79363) The cached values are not needed anymore since Emacs now caches the compiled queries. * lisp/progmodes/cmake-ts-mode.el: (cmake-ts-mode--indent-rules-cached): Remove. (cmake-ts-mode--indent-rules): Return values directly. (cmake-ts-mode--font-lock-settings-cached): Remove. (cmake-ts-mode--font-lock-settings): Return values directly. * lisp/progmodes/php-ts-mode.el: (php-ts-mode--font-lock-settings-cached): Remove. (php-ts-mode--font-lock-settings): Return values directly. (php-ts-mode--custom-html-font-lock-settings-cached): Remove. (php-ts-mode--custom-html-font-lock-settings): Return values directly. * lisp/textmodes/mhtml-ts-mode.el: (mhtml-ts-mode--treesit-font-lock-settings-cached): Remove. (mhtml-ts-mode--treesit-font-lock-settings): Return values directly. (mhtml-ts-mode--treesit-indent-rules-cached): Remove. (mhtml-ts-mode--treesit-indent-rules): Return values directly. * lisp/progmodes/cmake-ts-mode.el: (cmake-ts-mode--indent-rules-cached): Remove (cmake-ts-mode--indent-rules): Return values directly. (cmake-ts-mode--font-lock-settings-cached): Remove. (cmake-ts-mode--font-lock-settings): Return values directly. * lisp/progmodes/csharp-mode.el: (csharp-ts-mode--font-lock-settings-cached): Remove. (csharp-ts-mode--font-lock-settings): Return values directly. diff --git a/lisp/progmodes/cmake-ts-mode.el b/lisp/progmodes/cmake-ts-mode.el index 60d03f50a12..8930f96a2cf 100644 --- a/lisp/progmodes/cmake-ts-mode.el +++ b/lisp/progmodes/cmake-ts-mode.el @@ -61,35 +61,27 @@ table) "Syntax table for `cmake-ts-mode'.") -(defvar cmake-ts-mode--indent-rules-cached nil - "Cached tree-sitter indent rules for `cmake-ts-mode'.") - (defun cmake-ts-mode--indent-rules () - "Return tree-sitter indent rules for `cmake-ts-mode'. - -Tree-sitter indent rules are evaluated the first time this function is -called. Subsequent calls return the first evaluated value." - (or cmake-ts-mode--indent-rules-cached - (setq cmake-ts-mode--indent-rules-cached - `((cmake - ((node-is ")") parent-bol 0) - ((node-is "else_command") parent-bol 0) - ((node-is "elseif_command") parent-bol 0) - ((node-is "endforeach_command") parent-bol 0) - ((node-is "endfunction_command") parent-bol 0) - ((node-is "endif_command") parent-bol 0) - ((parent-is "foreach_loop") parent-bol cmake-ts-mode-indent-offset) - ((parent-is "function_def") parent-bol cmake-ts-mode-indent-offset) - ((parent-is "if_condition") parent-bol cmake-ts-mode-indent-offset) - ((parent-is "normal_command") parent-bol cmake-ts-mode-indent-offset) - ;; Release v0.4.0 wraps arguments in an argument_list node. - ,@(ignore-errors - (treesit-query-capture 'cmake '((argument_list) @capture)) - `(((parent-is "argument_list") grand-parent cmake-ts-mode-indent-offset))) - ;; Release v0.3.0 wraps the body of commands into a body node. - ,@(ignore-errors - (treesit-query-capture 'cmake '((body) @capture)) - `(((parent-is "body") grand-parent cmake-ts-mode-indent-offset)))))))) + "Return tree-sitter indent rules for `cmake-ts-mode'." + `((cmake + ((node-is ")") parent-bol 0) + ((node-is "else_command") parent-bol 0) + ((node-is "elseif_command") parent-bol 0) + ((node-is "endforeach_command") parent-bol 0) + ((node-is "endfunction_command") parent-bol 0) + ((node-is "endif_command") parent-bol 0) + ((parent-is "foreach_loop") parent-bol cmake-ts-mode-indent-offset) + ((parent-is "function_def") parent-bol cmake-ts-mode-indent-offset) + ((parent-is "if_condition") parent-bol cmake-ts-mode-indent-offset) + ((parent-is "normal_command") parent-bol cmake-ts-mode-indent-offset) + ;; Release v0.4.0 wraps arguments in an argument_list node. + ,@(ignore-errors + (treesit-query-capture 'cmake '((argument_list) @capture)) + `(((parent-is "argument_list") grand-parent cmake-ts-mode-indent-offset))) + ;; Release v0.3.0 wraps the body of commands into a body node. + ,@(ignore-errors + (treesit-query-capture 'cmake '((body) @capture)) + `(((parent-is "body") grand-parent cmake-ts-mode-indent-offset)))))) (defvar cmake-ts-mode--constants '("ON" "TRUE" "YES" "Y" "OFF" "FALSE" "NO" "N" "IGNORE" "NOTFOUND") @@ -148,76 +140,68 @@ Check if a node type is available, then return the right font lock rules." eol)) @font-lock-constant-face)))))))) -(defvar cmake-ts-mode--font-lock-settings-cached nil - "Cached tree-sitter font-lock settings for `cmake-ts-mode'.") - (defun cmake-ts-mode--font-lock-settings () - "Return tree-sitter font-lock settings for `cmake-ts-mode'. - -Tree-sitter font-lock settings are evaluated the first time this -function is called. Subsequent calls return the first evaluated value." - (or cmake-ts-mode--font-lock-settings-cached - (setq cmake-ts-mode--font-lock-settings-cached - (treesit-font-lock-rules - :language 'cmake - :feature 'bracket - '((["(" ")"]) @font-lock-bracket-face) - - :language 'cmake - :feature 'builtin - (cmake-ts-mode--font-lock-compatibility-fe9b5e0) - - :language 'cmake - :feature 'comment - '([(bracket_comment) (line_comment)] @font-lock-comment-face) - - :language 'cmake - :feature 'constant - `(((argument) @font-lock-constant-face - (:match ,(rx-to-string - `(seq bol - (or ,@cmake-ts-mode--constants) - eol)) - @font-lock-constant-face))) - - :language 'cmake - :feature 'function - '((normal_command (identifier) @font-lock-function-call-face)) - - :language 'cmake - :feature 'keyword - `([,@cmake-ts-mode--keywords] @font-lock-keyword-face) - - :language 'cmake - :feature 'number - '(((unquoted_argument) @font-lock-number-face - (:match "\\`-?[[:digit:]]*\\.?[[:digit:]]*\\.?[[:digit:]]+\\'" - @font-lock-number-face))) - - :language 'cmake - :feature 'string - '([(bracket_argument) (quoted_argument)] @font-lock-string-face) - - :language 'cmake - :feature 'escape-sequence - :override t - '((escape_sequence) @font-lock-escape-face) - - :language 'cmake - :feature 'misc-punctuation - ;; Don't override strings. - :override 'nil - '((["$" "{" "}"]) @font-lock-misc-punctuation-face) - - :language 'cmake - :feature 'variable - :override t - '((variable) @font-lock-variable-use-face) - - :language 'cmake - :feature 'error - :override t - '((ERROR) @font-lock-warning-face))))) + "Return tree-sitter font-lock settings for `cmake-ts-mode'." + (treesit-font-lock-rules + :language 'cmake + :feature 'bracket + '((["(" ")"]) @font-lock-bracket-face) + + :language 'cmake + :feature 'builtin + (cmake-ts-mode--font-lock-compatibility-fe9b5e0) + + :language 'cmake + :feature 'comment + '([(bracket_comment) (line_comment)] @font-lock-comment-face) + + :language 'cmake + :feature 'constant + `(((argument) @font-lock-constant-face + (:match ,(rx-to-string + `(seq bol + (or ,@cmake-ts-mode--constants) + eol)) + @font-lock-constant-face))) + + :language 'cmake + :feature 'function + '((normal_command (identifier) @font-lock-function-call-face)) + + :language 'cmake + :feature 'keyword + `([,@cmake-ts-mode--keywords] @font-lock-keyword-face) + + :language 'cmake + :feature 'number + '(((unquoted_argument) @font-lock-number-face + (:match "\\`-?[[:digit:]]*\\.?[[:digit:]]*\\.?[[:digit:]]+\\'" + @font-lock-number-face))) + + :language 'cmake + :feature 'string + '([(bracket_argument) (quoted_argument)] @font-lock-string-face) + + :language 'cmake + :feature 'escape-sequence + :override t + '((escape_sequence) @font-lock-escape-face) + + :language 'cmake + :feature 'misc-punctuation + ;; Don't override strings. + :override 'nil + '((["$" "{" "}"]) @font-lock-misc-punctuation-face) + + :language 'cmake + :feature 'variable + :override t + '((variable) @font-lock-variable-use-face) + + :language 'cmake + :feature 'error + :override t + '((ERROR) @font-lock-warning-face))) (defun cmake-ts-mode--defun-name (node) "Return the defun name of NODE. diff --git a/lisp/progmodes/csharp-mode.el b/lisp/progmodes/csharp-mode.el index 5656b5d00ba..1a31e3f9ab9 100644 --- a/lisp/progmodes/csharp-mode.el +++ b/lisp/progmodes/csharp-mode.el @@ -779,345 +779,337 @@ compilation and evaluation time conflicts." (if (csharp-ts-mode--test-method-declaration-type-field) 'type: 'returns:)) -(defvar csharp-ts-mode--font-lock-settings-cached nil - "Cached tree-sitter font-lock settings for `csharp-ts-mode'.") - (defun csharp-ts-mode--font-lock-settings () - "Return tree-sitter font-lock settings for `csharp-ts-mode'. - -Tree-sitter font-lock settings are evaluated the first time this -function is called. Subsequent calls return the first evaluated value." - (or csharp-ts-mode--font-lock-settings-cached - (setq csharp-ts-mode--font-lock-settings-cached - (treesit-font-lock-rules - :language 'c-sharp - :feature 'expression - '((conditional_expression (identifier) @font-lock-variable-use-face) - (postfix_unary_expression (identifier)* @font-lock-variable-use-face) - (initializer_expression (assignment_expression left: (identifier) @font-lock-property-use-face)) - (anonymous_object_creation_expression - (identifier) @font-lock-property-use-face - (identifier) @font-lock-variable-use-face) - (anonymous_object_creation_expression - (identifier) @font-lock-property-use-face - [(object_creation_expression) - (integer_literal) - (string_literal) - (binary_expression) - (invocation_expression) - (member_access_expression) - (conditional_expression)]) - (interpolated_string_expression - (interpolation - (identifier) @font-lock-variable-use-face)) - (interpolated_string_expression - (interpolation - (member_access_expression - name: (identifier) @font-lock-property-use-face))) - ((interpolated_string_expression - (interpolation - (member_access_expression - expression: (identifier) @font-lock-variable-use-face))) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - ((element_access_expression (identifier) @font-lock-variable-use-face) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - ((element_access_expression (identifier) @font-lock-variable-use-face) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - ((return_statement (identifier) @font-lock-variable-use-face) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - ((return_statement (member_access_expression - expression: (identifier) @font-lock-variable-use-face)) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - ((is_pattern_expression - expression: (identifier) @font-lock-variable-use-face) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - ((is_pattern_expression - expression: (member_access_expression - expression: (identifier) @font-lock-variable-use-face)) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - (is_pattern_expression - expression: (member_access_expression - name: (identifier) @font-lock-property-use-face)) - (is_pattern_expression - pattern: (constant_pattern (identifier) @font-lock-type-face)) - (is_pattern_expression - pattern: (constant_pattern (member_access_expression - name: (identifier) @font-lock-type-face))) - ((binary_expression - left: (identifier) @font-lock-variable-use-face) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - ((binary_expression - right: (identifier) @font-lock-variable-use-face) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - (assignment_expression - right: (identifier) @font-lock-variable-use-face) - (expression_statement ;; capture parent node to NOT shadow variable_declaration. - (assignment_expression - left: (identifier) @font-lock-variable-use-face)) - (if_statement condition: (identifier) @font-lock-variable-use-face) - - ;; handle more specific matchers before generalized variable-use fallback. - (invocation_expression - function: (member_access_expression - name: (identifier) @font-lock-function-call-face)) - (invocation_expression - function: (member_access_expression - name: (generic_name (identifier) @font-lock-function-call-face))) - (member_access_expression - expression: (identifier) @font-lock-variable-use-face + "Return tree-sitter font-lock settings for `csharp-ts-mode'." + (treesit-font-lock-rules + :language 'c-sharp + :feature 'expression + '((conditional_expression (identifier) @font-lock-variable-use-face) + (postfix_unary_expression (identifier)* @font-lock-variable-use-face) + (initializer_expression (assignment_expression left: (identifier) @font-lock-property-use-face)) + (anonymous_object_creation_expression + (identifier) @font-lock-property-use-face + (identifier) @font-lock-variable-use-face) + (anonymous_object_creation_expression + (identifier) @font-lock-property-use-face + [(object_creation_expression) + (integer_literal) + (string_literal) + (binary_expression) + (invocation_expression) + (member_access_expression) + (conditional_expression)]) + (interpolated_string_expression + (interpolation + (identifier) @font-lock-variable-use-face)) + (interpolated_string_expression + (interpolation + (member_access_expression + name: (identifier) @font-lock-property-use-face))) + ((interpolated_string_expression + (interpolation + (member_access_expression + expression: (identifier) @font-lock-variable-use-face))) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + ((element_access_expression (identifier) @font-lock-variable-use-face) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + ((element_access_expression (identifier) @font-lock-variable-use-face) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + ((return_statement (identifier) @font-lock-variable-use-face) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + ((return_statement (member_access_expression + expression: (identifier) @font-lock-variable-use-face)) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + ((is_pattern_expression + expression: (identifier) @font-lock-variable-use-face) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + ((is_pattern_expression + expression: (member_access_expression + expression: (identifier) @font-lock-variable-use-face)) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + (is_pattern_expression + expression: (member_access_expression + name: (identifier) @font-lock-property-use-face)) + (is_pattern_expression + pattern: (constant_pattern (identifier) @font-lock-type-face)) + (is_pattern_expression + pattern: (constant_pattern (member_access_expression + name: (identifier) @font-lock-type-face))) + ((binary_expression + left: (identifier) @font-lock-variable-use-face) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + ((binary_expression + right: (identifier) @font-lock-variable-use-face) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + (assignment_expression + right: (identifier) @font-lock-variable-use-face) + (expression_statement ;; capture parent node to NOT shadow variable_declaration. + (assignment_expression + left: (identifier) @font-lock-variable-use-face)) + (if_statement condition: (identifier) @font-lock-variable-use-face) + + ;; handle more specific matchers before generalized variable-use fallback. + (invocation_expression + function: (member_access_expression + name: (identifier) @font-lock-function-call-face)) + (invocation_expression + function: (member_access_expression + name: (generic_name (identifier) @font-lock-function-call-face))) + (member_access_expression + expression: (identifier) @font-lock-variable-use-face + name: (identifier) @font-lock-property-use-face)) + + :language 'c-sharp + :feature 'bracket + '((["(" ")" "[" "]" "{" "}" (interpolation_brace)]) @font-lock-bracket-face) + + :language 'c-sharp + :feature 'delimiter + '((["," ":" ";"]) @font-lock-delimiter-face) + + :language 'c-sharp + :feature 'error + '((ERROR) @font-lock-warning-face) + + :language 'c-sharp + :override t + :feature 'comment + '((comment) @font-lock-comment-face) + + :language 'c-sharp + :override t + :feature 'keyword + `([,@csharp-ts-mode--keywords] @font-lock-keyword-face + (modifier) @font-lock-keyword-face + ,@(if (csharp-ts-mode--test-this-expression) + '((this_expression) @font-lock-keyword-face) + '("this" @font-lock-keyword-face)) + + ;; avoid fontifying indentifiers with a keyword-values as identifiers. + ((identifier) @font-lock-keyword-face + (:match ,(concat "\\`" (regexp-opt csharp-ts-mode--keywords t) "\\'") @font-lock-keyword-face))) + + :language 'c-sharp + :override t + :feature 'attribute + `((attribute_list + "[" @csharp-ts-mode-attribute-face + (attribute name: (identifier) @csharp-ts-mode-attribute-face) + "]" @csharp-ts-mode-attribute-face)) + + :language 'c-sharp + :override t + :feature 'escape-sequence + '((escape_sequence) @font-lock-escape-face) + + :language 'c-sharp + :override t + :feature 'literal + `((integer_literal) @font-lock-number-face + (real_literal) @font-lock-number-face + (null_literal) @font-lock-constant-face + (boolean_literal) @font-lock-constant-face) + + :language 'c-sharp + :feature 'string + `([(string_literal) + (verbatim_string_literal) + ,@ (when (csharp-ts-mode--test-string-content) + '((string_content) + "\"")) + ,@(if (csharp-ts-mode--test-interpolated-string-text) + '((interpolated_string_text) + (interpolated_verbatim_string_text) + (character_literal) + "\"" + "$\"" + "@$\"" + "$@\"") + '((interpolation_start) + (interpolation_quote)))] + @font-lock-string-face) + + :language 'c-sharp + :feature 'type + `((predefined_type) @font-lock-type-face + (implicit_type) @font-lock-type-face + (nullable_type) @font-lock-type-face + (type_parameter + (identifier) @font-lock-type-face) + (type_argument_list + (identifier) @font-lock-type-face) + (type_argument_list + (generic_name + (identifier) @font-lock-type-face)) + (base_list + (generic_name + (identifier) @font-lock-type-face)) + (array_type + (identifier) @font-lock-type-face) + (qualified_name + name: (generic_name (identifier) @font-lock-type-face)) + (cast_expression (identifier) @font-lock-type-face) + (cast_expression (generic_name (identifier) @font-lock-type-face)) + ["operator"] @font-lock-type-face + (type_parameter_constraints_clause + (identifier) @font-lock-type-face) + ,@(if (csharp-ts-mode--test-type-constraint) + '((type_constraint type: (identifier) @font-lock-type-face) + (type_constraint type: (generic_name (identifier) @font-lock-type-face))) + '((type_parameter_constraint (type type: (identifier) @font-lock-type-face)) + (type_parameter_constraint (type type: (generic_name (identifier) @font-lock-type-face))))) + + ,@(when (csharp-ts-mode--test-type-of-expression) + '((type_of_expression (identifier) @font-lock-type-face))) + + ,@(when (csharp-ts-mode--test-typeof-expression) + '((typeof_expression (identifier) @font-lock-type-face))) + + (object_creation_expression + type: (identifier) @font-lock-type-face) + (object_creation_expression + type: (generic_name (identifier) @font-lock-type-face)) + (as_expression right: (identifier) @font-lock-type-face) + (as_expression right: (generic_name (identifier) @font-lock-type-face))) + + :language 'c-sharp + :feature 'definition + `((qualified_name (identifier) @font-lock-type-face) + (using_directive (identifier) @font-lock-type-face) + ,@(when (csharp-ts-mode--test-name-equals) + '((using_directive (name_equals + (identifier) @font-lock-type-face)))) + + (enum_declaration (identifier) @font-lock-type-face) + (enum_member_declaration (identifier) @font-lock-variable-name-face) + (field_declaration (variable_declaration (variable_declarator + name: (identifier) @font-lock-variable-name-face))) + + (interface_declaration (identifier) @font-lock-type-face) + + (struct_declaration (identifier) @font-lock-type-face) + + (record_declaration (identifier) @font-lock-type-face) + (namespace_declaration (identifier) @font-lock-type-face) + (base_list (identifier) @font-lock-type-face) + (property_declaration + type: (nullable_type) @font-lock-type-face + name: (identifier) @font-lock-variable-name-face) + (property_declaration + type: (predefined_type) @font-lock-type-face + name: (identifier) @font-lock-variable-name-face) + (property_declaration + type: (identifier) @font-lock-type-face + name: (identifier) @font-lock-variable-name-face) + (class_declaration (identifier) @font-lock-type-face) + + (constructor_declaration name: (_) @font-lock-type-face) + ;; Handle different releases of tree-sitter-c-sharp. + ;; Check if keyword void_keyword is available, then return the correct rule." + ,@(condition-case nil + (progn (treesit-query-capture 'csharp '((void_keyword) @capture)) + `((method_declaration ,csharp-ts-mode--type-field [(identifier) (void_keyword)] @font-lock-type-face))) + (error + `((method_declaration ,csharp-ts-mode--type-field [(identifier) (predefined_type)] @font-lock-type-face)))) + (method_declaration ,csharp-ts-mode--type-field (generic_name (identifier) @font-lock-type-face)) + (method_declaration name: (_) @font-lock-function-name-face) + + ;; only fontify known expression-types, to avoid the need to use :override + ;; for lambda-expressions in 'function below. + (variable_declarator + name: (identifier) @font-lock-variable-name-face + [(object_creation_expression) + (integer_literal) + (string_literal) + (binary_expression) + (invocation_expression) + (await_expression) + (member_access_expression) + (conditional_expression)]) + + (catch_declaration + ((identifier) @font-lock-type-face)) + (catch_declaration + ((identifier) @font-lock-type-face + (identifier) @font-lock-variable-name-face)) + + (variable_declaration (identifier) @font-lock-type-face) + (variable_declaration (qualified_name + name: (generic_name (identifier) @font-lock-type-face))) + (variable_declaration (generic_name (identifier) @font-lock-type-face)) + + (parameter type: (identifier) @font-lock-type-face) + (parameter type: (generic_name (identifier) @font-lock-type-face)) + (parameter name: (identifier) @font-lock-variable-name-face) + + (lambda_expression (identifier) @font-lock-variable-name-face) + (lambda_expression + parameters: (implicit_parameter) @font-lock-variable-name-face) + + (declaration_expression type: (identifier) @font-lock-type-face) + (declaration_expression name: (identifier) @font-lock-variable-name-face)) + + :language 'c-sharp + :feature 'function + '((invocation_expression + function: (identifier) @font-lock-function-call-face) + ((invocation_expression + function: (member_access_expression + expression: (identifier) @font-lock-variable-use-face)) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + (argument (identifier) @font-lock-variable-use-face) + ((argument (member_access_expression + expression: (identifier) @font-lock-variable-use-face)) + (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) + (argument (member_access_expression name: (identifier) @font-lock-property-use-face)) - - :language 'c-sharp - :feature 'bracket - '((["(" ")" "[" "]" "{" "}" (interpolation_brace)]) @font-lock-bracket-face) - - :language 'c-sharp - :feature 'delimiter - '((["," ":" ";"]) @font-lock-delimiter-face) - - :language 'c-sharp - :feature 'error - '((ERROR) @font-lock-warning-face) - - :language 'c-sharp - :override t - :feature 'comment - '((comment) @font-lock-comment-face) - - :language 'c-sharp - :override t - :feature 'keyword - `([,@csharp-ts-mode--keywords] @font-lock-keyword-face - (modifier) @font-lock-keyword-face - ,@(if (csharp-ts-mode--test-this-expression) - '((this_expression) @font-lock-keyword-face) - '("this" @font-lock-keyword-face)) - - ;; avoid fontifying indentifiers with a keyword-values as identifiers. - ((identifier) @font-lock-keyword-face - (:match ,(concat "\\`" (regexp-opt csharp-ts-mode--keywords t) "\\'") @font-lock-keyword-face))) - - :language 'c-sharp - :override t - :feature 'attribute - `((attribute_list - "[" @csharp-ts-mode-attribute-face - (attribute name: (identifier) @csharp-ts-mode-attribute-face) - "]" @csharp-ts-mode-attribute-face)) - - :language 'c-sharp - :override t - :feature 'escape-sequence - '((escape_sequence) @font-lock-escape-face) - - :language 'c-sharp - :override t - :feature 'literal - `((integer_literal) @font-lock-number-face - (real_literal) @font-lock-number-face - (null_literal) @font-lock-constant-face - (boolean_literal) @font-lock-constant-face) - - :language 'c-sharp - :feature 'string - `([(string_literal) - (verbatim_string_literal) - ,@ (when (csharp-ts-mode--test-string-content) - '((string_content) - "\"")) - ,@(if (csharp-ts-mode--test-interpolated-string-text) - '((interpolated_string_text) - (interpolated_verbatim_string_text) - (character_literal) - "\"" - "$\"" - "@$\"" - "$@\"") - '((interpolation_start) - (interpolation_quote)))] - @font-lock-string-face) - - :language 'c-sharp - :feature 'type - `((predefined_type) @font-lock-type-face - (implicit_type) @font-lock-type-face - (nullable_type) @font-lock-type-face - (type_parameter - (identifier) @font-lock-type-face) - (type_argument_list - (identifier) @font-lock-type-face) - (type_argument_list - (generic_name - (identifier) @font-lock-type-face)) - (base_list - (generic_name - (identifier) @font-lock-type-face)) - (array_type - (identifier) @font-lock-type-face) - (qualified_name - name: (generic_name (identifier) @font-lock-type-face)) - (cast_expression (identifier) @font-lock-type-face) - (cast_expression (generic_name (identifier) @font-lock-type-face)) - ["operator"] @font-lock-type-face - (type_parameter_constraints_clause - (identifier) @font-lock-type-face) - ,@(if (csharp-ts-mode--test-type-constraint) - '((type_constraint type: (identifier) @font-lock-type-face) - (type_constraint type: (generic_name (identifier) @font-lock-type-face))) - '((type_parameter_constraint (type type: (identifier) @font-lock-type-face)) - (type_parameter_constraint (type type: (generic_name (identifier) @font-lock-type-face))))) - - ,@(when (csharp-ts-mode--test-type-of-expression) - '((type_of_expression (identifier) @font-lock-type-face))) - - ,@(when (csharp-ts-mode--test-typeof-expression) - '((typeof_expression (identifier) @font-lock-type-face))) - - (object_creation_expression - type: (identifier) @font-lock-type-face) - (object_creation_expression - type: (generic_name (identifier) @font-lock-type-face)) - (as_expression right: (identifier) @font-lock-type-face) - (as_expression right: (generic_name (identifier) @font-lock-type-face))) - - :language 'c-sharp - :feature 'definition - `((qualified_name (identifier) @font-lock-type-face) - (using_directive (identifier) @font-lock-type-face) - ,@(when (csharp-ts-mode--test-name-equals) - '((using_directive (name_equals - (identifier) @font-lock-type-face)))) - - (enum_declaration (identifier) @font-lock-type-face) - (enum_member_declaration (identifier) @font-lock-variable-name-face) - (field_declaration (variable_declaration (variable_declarator - name: (identifier) @font-lock-variable-name-face))) - - (interface_declaration (identifier) @font-lock-type-face) - - (struct_declaration (identifier) @font-lock-type-face) - - (record_declaration (identifier) @font-lock-type-face) - (namespace_declaration (identifier) @font-lock-type-face) - (base_list (identifier) @font-lock-type-face) - (property_declaration - type: (nullable_type) @font-lock-type-face - name: (identifier) @font-lock-variable-name-face) - (property_declaration - type: (predefined_type) @font-lock-type-face - name: (identifier) @font-lock-variable-name-face) - (property_declaration - type: (identifier) @font-lock-type-face - name: (identifier) @font-lock-variable-name-face) - (class_declaration (identifier) @font-lock-type-face) - - (constructor_declaration name: (_) @font-lock-type-face) - ;; Handle different releases of tree-sitter-c-sharp. - ;; Check if keyword void_keyword is available, then return the correct rule." - ,@(condition-case nil - (progn (treesit-query-capture 'csharp '((void_keyword) @capture)) - `((method_declaration ,csharp-ts-mode--type-field [(identifier) (void_keyword)] @font-lock-type-face))) - (error - `((method_declaration ,csharp-ts-mode--type-field [(identifier) (predefined_type)] @font-lock-type-face)))) - (method_declaration ,csharp-ts-mode--type-field (generic_name (identifier) @font-lock-type-face)) - (method_declaration name: (_) @font-lock-function-name-face) - - ;; only fontify known expression-types, to avoid the need to use :override - ;; for lambda-expressions in 'function below. - (variable_declarator - name: (identifier) @font-lock-variable-name-face - [(object_creation_expression) - (integer_literal) - (string_literal) - (binary_expression) - (invocation_expression) - (await_expression) - (member_access_expression) - (conditional_expression)]) - - (catch_declaration - ((identifier) @font-lock-type-face)) - (catch_declaration - ((identifier) @font-lock-type-face - (identifier) @font-lock-variable-name-face)) - - (variable_declaration (identifier) @font-lock-type-face) - (variable_declaration (qualified_name - name: (generic_name (identifier) @font-lock-type-face))) - (variable_declaration (generic_name (identifier) @font-lock-type-face)) - - (parameter type: (identifier) @font-lock-type-face) - (parameter type: (generic_name (identifier) @font-lock-type-face)) - (parameter name: (identifier) @font-lock-variable-name-face) - - (lambda_expression (identifier) @font-lock-variable-name-face) - (lambda_expression - parameters: (implicit_parameter) @font-lock-variable-name-face) - - (declaration_expression type: (identifier) @font-lock-type-face) - (declaration_expression name: (identifier) @font-lock-variable-name-face)) - - :language 'c-sharp - :feature 'function - '((invocation_expression - function: (identifier) @font-lock-function-call-face) - ((invocation_expression - function: (member_access_expression - expression: (identifier) @font-lock-variable-use-face)) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - (argument (identifier) @font-lock-variable-use-face) - ((argument (member_access_expression - expression: (identifier) @font-lock-variable-use-face)) - (:match "^[a-z][A-Za-z0-9]+" @font-lock-variable-use-face)) - (argument (member_access_expression - name: (identifier) @font-lock-property-use-face)) - ;; only highlight as function if variable contains lambda expression - (variable_declarator - name: (identifier) @font-lock-function-name-face - (lambda_expression))) - - :language 'c-sharp - :feature 'directives - :override t - (if (csharp-ts-mode--test-if-directive) - '((if_directive - "if" @font-lock-preprocessor-face - (identifier) @font-lock-variable-use-face) - (elif_directive - "elif" @font-lock-preprocessor-face - (identifier) @font-lock-variable-use-face) - (else_directive) @font-lock-preprocessor-face - (endif_directive) @font-lock-preprocessor-face - (define_directive - "define" @font-lock-preprocessor-face - (identifier) @font-lock-variable-use-face) - (nullable_directive) @font-lock-preprocessor-face - (pragma_directive) @font-lock-preprocessor-face - (region_directive) @font-lock-preprocessor-face - (endregion_directive) @font-lock-preprocessor-face - (region_directive - (preproc_message) @font-lock-variable-use-face) - (endregion_directive - (preproc_message) @font-lock-variable-use-face)) - '((preproc_if - "#if" @font-lock-preprocessor-face - (identifier) @font-lock-variable-use-face) - (preproc_elif - "#elif" @font-lock-preprocessor-face - (identifier) @font-lock-variable-use-face) - (preproc_else) @font-lock-preprocessor-face - "#endif" @font-lock-preprocessor-face - (preproc_define - "#define" @font-lock-preprocessor-face - (preproc_arg) @font-lock-variable-use-face) - (preproc_nullable) @font-lock-preprocessor-face - (preproc_pragma) @font-lock-preprocessor-face - (preproc_region) @font-lock-preprocessor-face - (preproc_endregion) @font-lock-preprocessor-face - (preproc_region - (preproc_arg) @font-lock-variable-use-face) - (preproc_endregion - (preproc_arg) @font-lock-variable-use-face))))))) + ;; only highlight as function if variable contains lambda expression + (variable_declarator + name: (identifier) @font-lock-function-name-face + (lambda_expression))) + + :language 'c-sharp + :feature 'directives + :override t + (if (csharp-ts-mode--test-if-directive) + '((if_directive + "if" @font-lock-preprocessor-face + (identifier) @font-lock-variable-use-face) + (elif_directive + "elif" @font-lock-preprocessor-face + (identifier) @font-lock-variable-use-face) + (else_directive) @font-lock-preprocessor-face + (endif_directive) @font-lock-preprocessor-face + (define_directive + "define" @font-lock-preprocessor-face + (identifier) @font-lock-variable-use-face) + (nullable_directive) @font-lock-preprocessor-face + (pragma_directive) @font-lock-preprocessor-face + (region_directive) @font-lock-preprocessor-face + (endregion_directive) @font-lock-preprocessor-face + (region_directive + (preproc_message) @font-lock-variable-use-face) + (endregion_directive + (preproc_message) @font-lock-variable-use-face)) + '((preproc_if + "#if" @font-lock-preprocessor-face + (identifier) @font-lock-variable-use-face) + (preproc_elif + "#elif" @font-lock-preprocessor-face + (identifier) @font-lock-variable-use-face) + (preproc_else) @font-lock-preprocessor-face + "#endif" @font-lock-preprocessor-face + (preproc_define + "#define" @font-lock-preprocessor-face + (preproc_arg) @font-lock-variable-use-face) + (preproc_nullable) @font-lock-preprocessor-face + (preproc_pragma) @font-lock-preprocessor-face + (preproc_region) @font-lock-preprocessor-face + (preproc_endregion) @font-lock-preprocessor-face + (preproc_region + (preproc_arg) @font-lock-variable-use-face) + (preproc_endregion + (preproc_arg) @font-lock-variable-use-face))))) ;;;###autoload (add-to-list 'auto-mode-alist '("\\.cs\\'" . csharp-mode)) diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index 15304486a5b..8d37bb53988 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el @@ -3513,72 +3513,64 @@ characters of the current line." node parent bol args) js-indent-level))) -(defvar js--treesit-indent-rules-cached nil - "Cached tree-sitter indent rules for `js-ts-mode'.") - (defun js--treesit-indent-rules () - "Return tree-sitter indent rules for `js-ts-mode'. - -Tree-sitter indent rules are evaluated the first time this function is -called. Subsequent calls return the first evaluated value." - (or js--treesit-indent-rules-cached - (setq js--treesit-indent-rules-cached - `((javascript - ((parent-is "program") parent-bol 0) - ((node-is "}") standalone-parent 0) - ((node-is ")") parent-bol 0) - ((node-is "]") parent-bol 0) - ((node-is ">") parent-bol 0) - ((and (parent-is "comment") c-ts-common-looking-at-star) - c-ts-common-comment-start-after-first-star -1) - ((parent-is "comment") prev-adaptive-prefix 0) - ((n-p-gp "identifier" "ternary_expression" "parenthesized_expression") - parent 0) - ((parent-is "ternary_expression") parent-bol js-indent-level) - ((parent-is "sequence_expression") parent 0) - ((parent-is "member_expression") js--treesit-member-chained-expression-helper 0) - ((parent-is "named_imports") parent-bol js-indent-level) - ((parent-is "statement_block") standalone-parent js-indent-level) - ((parent-is "variable_declarator") parent 0) - ((parent-is "arguments") parent-bol js-indent-level) - ((parent-is "array") parent-bol js-indent-level) - ((parent-is "formal_parameters") parent-bol js-indent-level) - ((parent-is "template_string") no-indent) ; Don't indent the string contents. - ((parent-is "template_substitution") parent-bol js-indent-level) - ((parent-is "object_pattern") parent-bol js-indent-level) - ((parent-is "object") parent-bol js-indent-level) - ((parent-is "pair") parent-bol js-indent-level) - ((parent-is "arrow_function") js--treesit-arrow-function-helper 0) - ((parent-is "parenthesized_expression") parent-bol js-indent-level) - ((parent-is "binary_expression") parent-bol js-indent-level) - ((parent-is "assignment_expression") parent-bol js-indent-level) - ((parent-is "class_body") parent-bol js-indent-level) - ;; "{" on the newline, should stay here. - ((node-is "statement_block") parent-bol 0) - ((parent-is "switch_statement") parent-bol 0) - ((parent-is "switch_body") js--treesit-switch-body-helper 0) - ((parent-is ,(rx "switch_" (or "case" "default"))) parent-bol js-indent-level) - ((match "while" "do_statement") parent-bol 0) - ((match "else" "if_statement") parent-bol 0) - ((parent-is ,(rx (or (seq (or "if" "for" "for_in" "while" "do") "_statement") - "else_clause"))) - parent-bol js-indent-level) - - ;; JSX - ,@(js-jsx--treesit-indent-compatibility-bb1f97b) - ((node-is "jsx_closing_element") parent 0) - ((match "jsx_element" "statement") parent js-indent-level) - ((parent-is "jsx_element") parent js-indent-level) - ((parent-is "jsx_text") parent-bol 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) - ;; FIXME(Theo): This no-node catch-all should be removed. When is it needed? - (no-node parent-bol 0)) - (jsdoc - ((and (parent-is "document") c-ts-common-looking-at-star) - c-ts-common-comment-start-after-first-star -1)))))) + "Return tree-sitter indent rules for `js-ts-mode'." + `((javascript + ((parent-is "program") parent-bol 0) + ((node-is "}") standalone-parent 0) + ((node-is ")") parent-bol 0) + ((node-is "]") parent-bol 0) + ((node-is ">") parent-bol 0) + ((and (parent-is "comment") c-ts-common-looking-at-star) + c-ts-common-comment-start-after-first-star -1) + ((parent-is "comment") prev-adaptive-prefix 0) + ((n-p-gp "identifier" "ternary_expression" "parenthesized_expression") + parent 0) + ((parent-is "ternary_expression") parent-bol js-indent-level) + ((parent-is "sequence_expression") parent 0) + ((parent-is "member_expression") js--treesit-member-chained-expression-helper 0) + ((parent-is "named_imports") parent-bol js-indent-level) + ((parent-is "statement_block") standalone-parent js-indent-level) + ((parent-is "variable_declarator") parent 0) + ((parent-is "arguments") parent-bol js-indent-level) + ((parent-is "array") parent-bol js-indent-level) + ((parent-is "formal_parameters") parent-bol js-indent-level) + ((parent-is "template_string") no-indent) ; Don't indent the string contents. + ((parent-is "template_substitution") parent-bol js-indent-level) + ((parent-is "object_pattern") parent-bol js-indent-level) + ((parent-is "object") parent-bol js-indent-level) + ((parent-is "pair") parent-bol js-indent-level) + ((parent-is "arrow_function") js--treesit-arrow-function-helper 0) + ((parent-is "parenthesized_expression") parent-bol js-indent-level) + ((parent-is "binary_expression") parent-bol js-indent-level) + ((parent-is "assignment_expression") parent-bol js-indent-level) + ((parent-is "class_body") parent-bol js-indent-level) + ;; "{" on the newline, should stay here. + ((node-is "statement_block") parent-bol 0) + ((parent-is "switch_statement") parent-bol 0) + ((parent-is "switch_body") js--treesit-switch-body-helper 0) + ((parent-is ,(rx "switch_" (or "case" "default"))) parent-bol js-indent-level) + ((match "while" "do_statement") parent-bol 0) + ((match "else" "if_statement") parent-bol 0) + ((parent-is ,(rx (or (seq (or "if" "for" "for_in" "while" "do") "_statement") + "else_clause"))) + parent-bol js-indent-level) + + ;; JSX + ,@(js-jsx--treesit-indent-compatibility-bb1f97b) + ((node-is "jsx_closing_element") parent 0) + ((match "jsx_element" "statement") parent js-indent-level) + ((parent-is "jsx_element") parent js-indent-level) + ((parent-is "jsx_text") parent-bol 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) + ;; FIXME(Theo): This no-node catch-all should be removed. When is it needed? + (no-node parent-bol 0)) + (jsdoc + ((and (parent-is "document") c-ts-common-looking-at-star) + c-ts-common-comment-start-after-first-star -1)))) (defvar js--treesit-keywords '("as" "async" "await" "break" "case" "catch" "class" "const" "continue" @@ -3595,162 +3587,154 @@ called. Subsequent calls return the first evaluated value." "&&" "||" "!") "JavaScript operators for tree-sitter font-locking.") -(defvar js--treesit-font-lock-settings-cached nil - "Cached tree-sitter font-lock settings for `js-ts-mode'.") - (defun js--treesit-font-lock-settings () - "Return tree-sitter font-lock settings for `js-ts-mode'. - -Tree-sitter font-lock settings are evaluated the first time this -function is called. Subsequent calls return the first evaluated value." - (or js--treesit-font-lock-settings-cached - (setq js--treesit-font-lock-settings-cached - (treesit-font-lock-rules - - :language 'javascript - :feature 'comment - '([(comment) (hash_bang_line)] @font-lock-comment-face) - - :language 'javascript - :feature 'constant - '(((identifier) @font-lock-constant-face - (:match "\\`[A-Z_][0-9A-Z_]*\\'" @font-lock-constant-face)) - - [(true) (false) (null)] @font-lock-constant-face) - - :language 'javascript - :feature 'keyword - `([,@js--treesit-keywords] @font-lock-keyword-face - [(this) (super)] @font-lock-keyword-face) - - :language 'javascript - :feature 'string - '((regex pattern: (regex_pattern)) @font-lock-regexp-face - (string) @font-lock-string-face) - - :language 'javascript - :feature 'string-interpolation - :override t - '((template_string) @js--fontify-template-string - (template_substitution ["${" "}"] @font-lock-misc-punctuation-face)) - - :language 'javascript - :feature 'definition - `(,@(js--treesit-font-lock-compatibility-definition-feature) - - (class - name: (identifier) @font-lock-type-face) - - (class_declaration - name: (identifier) @font-lock-type-face) - - (function_declaration - name: (identifier) @font-lock-function-name-face) - - (method_definition - name: (property_identifier) @font-lock-function-name-face) - - (formal_parameters - [(identifier) @font-lock-variable-name-face - (array_pattern (identifier) @font-lock-variable-name-face) - (object_pattern (shorthand_property_identifier_pattern) @font-lock-variable-name-face)]) - - (variable_declarator - name: (identifier) @font-lock-variable-name-face) - - (variable_declarator - name: [(array_pattern (identifier) @font-lock-variable-name-face) - (object_pattern - (shorthand_property_identifier_pattern) @font-lock-variable-name-face)]) - - ;; full module imports - (import_clause (identifier) @font-lock-variable-name-face) - ;; named imports with aliasing - (import_clause (named_imports (import_specifier - alias: (identifier) @font-lock-variable-name-face))) - ;; named imports without aliasing - (import_clause (named_imports (import_specifier - !alias - name: (identifier) @font-lock-variable-name-face))) - - ;; full namespace import (* as alias) - (import_clause (namespace_import (identifier) @font-lock-variable-name-face))) - - :language 'javascript - :feature 'assignment - '((assignment_expression - left: (_) @js--treesit-fontify-assignment-lhs)) - - :language 'javascript - :feature 'function - '((call_expression - function: [(identifier) @font-lock-function-call-face - (member_expression - property: - (property_identifier) @font-lock-function-call-face)])) - - :language 'javascript - :feature 'jsx - '((jsx_opening_element name: (_) @font-lock-function-call-face) - (jsx_closing_element name: (_) @font-lock-function-call-face) - (jsx_self_closing_element name: (_) @font-lock-function-call-face) - (jsx_attribute (property_identifier) @font-lock-constant-face)) - - :language 'javascript - :feature 'property - '(((property_identifier) @font-lock-property-use-face) - (pair value: (identifier) @font-lock-variable-use-face) - ((shorthand_property_identifier) @font-lock-property-use-face)) - - :language 'javascript - :feature 'number - '((number) @font-lock-number-face - ((identifier) @font-lock-number-face - (:match "\\`\\(?:NaN\\|Infinity\\)\\'" @font-lock-number-face))) - - :language 'javascript - :feature 'operator - `([,@js--treesit-operators] @font-lock-operator-face - (ternary_expression ["?" ":"] @font-lock-operator-face)) - - :language 'javascript - :feature 'bracket - '((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face) - - :language 'javascript - :feature 'delimiter - '((["," "." ";" ":"]) @font-lock-delimiter-face) - - :language 'javascript - :feature 'escape-sequence - :override t - '((escape_sequence) @font-lock-escape-face) - - ;; "document" should be first, to avoid overlap. - :language 'jsdoc - :override t - :feature 'document - '((document) @font-lock-doc-face) - - :language 'jsdoc - :override t - :feature 'keyword - '((tag_name) @font-lock-doc-markup-face) - - :language 'jsdoc - :override t - :feature 'bracket - '((["{" "}"]) @font-lock-bracket-face) - - :language 'jsdoc - :override t - :feature 'property - '((type) @font-lock-type-face) - - :language 'jsdoc - :override t - :feature 'definition - '((identifier) @font-lock-variable-name-face))))) + "Return tree-sitter font-lock settings for `js-ts-mode'." + (treesit-font-lock-rules + + :language 'javascript + :feature 'comment + '([(comment) (hash_bang_line)] @font-lock-comment-face) + + :language 'javascript + :feature 'constant + '(((identifier) @font-lock-constant-face + (:match "\\`[A-Z_][0-9A-Z_]*\\'" @font-lock-constant-face)) + + [(true) (false) (null)] @font-lock-constant-face) + + :language 'javascript + :feature 'keyword + `([,@js--treesit-keywords] @font-lock-keyword-face + [(this) (super)] @font-lock-keyword-face) + + :language 'javascript + :feature 'string + '((regex pattern: (regex_pattern)) @font-lock-regexp-face + (string) @font-lock-string-face) + + :language 'javascript + :feature 'string-interpolation + :override t + '((template_string) @js--fontify-template-string + (template_substitution ["${" "}"] @font-lock-misc-punctuation-face)) + + :language 'javascript + :feature 'definition + `(,@(js--treesit-font-lock-compatibility-definition-feature) + + (class + name: (identifier) @font-lock-type-face) + + (class_declaration + name: (identifier) @font-lock-type-face) + + (function_declaration + name: (identifier) @font-lock-function-name-face) + + (method_definition + name: (property_identifier) @font-lock-function-name-face) + + (formal_parameters + [(identifier) @font-lock-variable-name-face + (array_pattern (identifier) @font-lock-variable-name-face) + (object_pattern (shorthand_property_identifier_pattern) @font-lock-variable-name-face)]) + + (variable_declarator + name: (identifier) @font-lock-variable-name-face) + + (variable_declarator + name: [(array_pattern (identifier) @font-lock-variable-name-face) + (object_pattern + (shorthand_property_identifier_pattern) @font-lock-variable-name-face)]) + + ;; full module imports + (import_clause (identifier) @font-lock-variable-name-face) + ;; named imports with aliasing + (import_clause (named_imports (import_specifier + alias: (identifier) @font-lock-variable-name-face))) + ;; named imports without aliasing + (import_clause (named_imports (import_specifier + !alias + name: (identifier) @font-lock-variable-name-face))) + + ;; full namespace import (* as alias) + (import_clause (namespace_import (identifier) @font-lock-variable-name-face))) + + :language 'javascript + :feature 'assignment + '((assignment_expression + left: (_) @js--treesit-fontify-assignment-lhs)) + + :language 'javascript + :feature 'function + '((call_expression + function: [(identifier) @font-lock-function-call-face + (member_expression + property: + (property_identifier) @font-lock-function-call-face)])) + + :language 'javascript + :feature 'jsx + '((jsx_opening_element name: (_) @font-lock-function-call-face) + (jsx_closing_element name: (_) @font-lock-function-call-face) + (jsx_self_closing_element name: (_) @font-lock-function-call-face) + (jsx_attribute (property_identifier) @font-lock-constant-face)) + + :language 'javascript + :feature 'property + '(((property_identifier) @font-lock-property-use-face) + (pair value: (identifier) @font-lock-variable-use-face) + ((shorthand_property_identifier) @font-lock-property-use-face)) + + :language 'javascript + :feature 'number + '((number) @font-lock-number-face + ((identifier) @font-lock-number-face + (:match "\\`\\(?:NaN\\|Infinity\\)\\'" @font-lock-number-face))) + + :language 'javascript + :feature 'operator + `([,@js--treesit-operators] @font-lock-operator-face + (ternary_expression ["?" ":"] @font-lock-operator-face)) + + :language 'javascript + :feature 'bracket + '((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face) + + :language 'javascript + :feature 'delimiter + '((["," "." ";" ":"]) @font-lock-delimiter-face) + + :language 'javascript + :feature 'escape-sequence + :override t + '((escape_sequence) @font-lock-escape-face) + + ;; "document" should be first, to avoid overlap. + :language 'jsdoc + :override t + :feature 'document + '((document) @font-lock-doc-face) + + :language 'jsdoc + :override t + :feature 'keyword + '((tag_name) @font-lock-doc-markup-face) + + :language 'jsdoc + :override t + :feature 'bracket + '((["{" "}"]) @font-lock-bracket-face) + + :language 'jsdoc + :override t + :feature 'property + '((type) @font-lock-type-face) + + :language 'jsdoc + :override t + :feature 'definition + '((identifier) @font-lock-variable-name-face))) (defun js--fontify-template-string (node override start end &rest _) "Fontify template string but not substitution inside it. diff --git a/lisp/progmodes/php-ts-mode.el b/lisp/progmodes/php-ts-mode.el index ea06c3f9dc9..953c115e69e 100644 --- a/lisp/progmodes/php-ts-mode.el +++ b/lisp/progmodes/php-ts-mode.el @@ -955,244 +955,228 @@ characters of the current line." ("::" . ?∷)) "Value for `prettify-symbols-alist' in `php-ts-mode'.") -(defvar php-ts-mode--font-lock-settings-cached nil - "Cached tree-sitter font-lock settings for `php-ts-mode'.") - (defun php-ts-mode--font-lock-settings () - "Return tree-sitter font-lock settings for `php-ts-mode'. - -Tree-sitter font-lock settings are evaluated the first time this -function is called. Subsequent calls return the first evaluated value." - (or php-ts-mode--font-lock-settings-cached - (setq php-ts-mode--font-lock-settings-cached - (treesit-font-lock-rules - - :language 'php - :feature 'keyword - :override t - `([,@(php-ts-mode--keywords)] @font-lock-keyword-face - ,@(when (php-ts-mode--test-visibility-modifier-operation-p) - '((visibility_modifier (operation) @font-lock-builtin-face))) - (var_modifier) @font-lock-builtin-face) - - :language 'php - :feature 'comment - :override t - '((comment) @font-lock-comment-face) - - :language 'php - :feature 'constant - `((boolean) @font-lock-constant-face - (null) @font-lock-constant-face - ;; predefined constant or built in constant (part of PHP core) - ((name) @font-lock-builtin-face - (:match ,(rx-to-string - `(: bos (or ,@php-ts-mode--predefined-constant) eos)) - @font-lock-builtin-face)) - ;; user defined constant - ((name) @font-lock-constant-face - (:match "\\`_*[A-Z][0-9A-Z_]+\\'" @font-lock-constant-face)) - (const_declaration - (const_element (name) @font-lock-constant-face)) - ;; declare directive - (declare_directive ["strict_types" "encoding" "ticks"] @font-lock-constant-face)) - - :language 'php - :feature 'name - '((goto_statement (name) @font-lock-constant-face) - (named_label_statement (name) @font-lock-constant-face)) - - :language 'php - :feature 'delimiter - `((["," ":" ";" "\\"]) @font-lock-delimiter-face) - - :language 'php - :feature 'operator - `((error_suppression_expression "@" @font-lock-keyword-face) - [,@(php-ts-mode--operators)] @font-lock-operator-face) - - :language 'php - :feature 'variable-name - :override t - '(((name) @font-lock-keyword-face (:equal "this" @font-lock-keyword-face)) - (variable_name (name) @font-lock-variable-name-face) - (relative_scope ["parent" "self" "static"] @font-lock-builtin-face) - (relative_scope) @font-lock-constant-face - (dynamic_variable_name (name) @font-lock-variable-name-face) - (member_access_expression - name: (_) @font-lock-variable-name-face) - (scoped_property_access_expression - scope: (name) @font-lock-constant-face) - (nullsafe_member_access_expression (name) @font-lock-variable-name-face) - (error_suppression_expression (name) @font-lock-property-name-face)) - - :language 'php - :feature 'string - `(("\"") @font-lock-string-face - (encapsed_string) @font-lock-string-face - (string_content) @font-lock-string-face - (string) @font-lock-string-face) - - :language 'php - :feature 'literal - '((integer) @font-lock-number-face - (float) @font-lock-number-face - (heredoc identifier: (heredoc_start) @font-lock-constant-face) - (heredoc_body (string_content) @font-lock-string-face) - (heredoc end_tag: (heredoc_end) @font-lock-constant-face) - (nowdoc identifier: (heredoc_start) @font-lock-constant-face) - (nowdoc_body (nowdoc_string) @font-lock-string-face) - (nowdoc end_tag: (heredoc_end) @font-lock-constant-face) - (shell_command_expression) @font-lock-string-face) - - :language 'php - :feature 'type - :override t - '((union_type "|" @font-lock-operator-face) - (union_type) @font-lock-type-face - (bottom_type) @font-lock-type-face - (primitive_type) @font-lock-type-face - ((primitive_type) @font-lock-keyword-face - (:equal "callable" @font-lock-keyword-face)) - (cast_type) @font-lock-type-face - (named_type) @font-lock-type-face - (optional_type) @font-lock-type-face) - - :language 'php - :feature 'definition - :override t - `((php_tag) @font-lock-preprocessor-face - ,@(if (php-ts-mode--test-php-end-tag-p) - '((php_end_tag) @font-lock-preprocessor-face) - '(("?>") @font-lock-preprocessor-face)) - ;; Highlights identifiers in declarations. - (class_declaration - name: (_) @font-lock-type-face) - (class_interface_clause (name) @font-lock-type-face) - (interface_declaration - name: (_) @font-lock-type-face) - (trait_declaration - name: (_) @font-lock-type-face) - (enum_declaration - name: (_) @font-lock-type-face) - (function_definition - name: (_) @font-lock-function-name-face) - ,@(when (php-ts-mode--test-property-hook-p) - '((property_hook (name) @font-lock-function-name-face))) - (method_declaration - name: (_) @font-lock-function-name-face) - (method_declaration - name: (name) @font-lock-builtin-face - (:match ,(rx-to-string - `(: bos (or ,@php-ts-mode--class-magic-methods) eos)) - @font-lock-builtin-face)) - ("=>") @font-lock-keyword-face - (object_creation_expression - (name) @font-lock-type-face) - ,@(when (php-ts-mode--test-namespace-name-as-prefix-p) - '((namespace_name_as_prefix "\\" @font-lock-delimiter-face) - (namespace_name_as_prefix - (namespace_name (name)) @font-lock-type-face))) - ,@(if (php-ts-mode--test-namespace-aliasing-clause-p) - '((namespace_aliasing_clause (name) @font-lock-type-face)) - '((namespace_use_clause alias: (name) @font-lock-type-face))) - ,@(when (not (php-ts-mode--test-namespace-use-group-clause-p)) - '((namespace_use_group - (namespace_use_clause (name) @font-lock-type-face)))) - (namespace_use_clause (name) @font-lock-type-face) - (namespace_name "\\" @font-lock-delimiter-face) - (namespace_name (name) @font-lock-type-face) - (use_declaration (name) @font-lock-property-use-face) - (use_instead_of_clause (name) @font-lock-type-face) - (binary_expression - operator: "instanceof" - right: (name) @font-lock-type-face)) - - :language 'php - :feature 'function-scope - :override t - '((scoped_call_expression - scope: (name) @font-lock-constant-face) - (class_constant_access_expression (name) @font-lock-constant-face)) - - :language 'php - :feature 'function-call - :override t - '((function_call_expression - function: (name) @font-lock-function-call-face) - (scoped_call_expression - name: (name) @font-lock-function-call-face) - (member_call_expression - name: (name) @font-lock-function-call-face) - (nullsafe_member_call_expression - name: (_) @font-lock-function-call-face)) - - :language 'php - :feature 'argument - '((argument - name: (_) @font-lock-constant-face)) - - :language 'php - :feature 'escape-sequence - :override t - '((string (escape_sequence) @font-lock-escape-face) - (encapsed_string (escape_sequence) @font-lock-escape-face) - (heredoc_body (escape_sequence) @font-lock-escape-face)) - - :language 'php - :feature 'base-clause - :override t - `((base_clause (name) @font-lock-type-face) - (use_as_clause (name) @font-lock-property-use-face) - ,@(when (not (php-ts-mode--test-namespace-name-as-prefix-p)) - '((qualified_name prefix: "\\" @font-lock-delimiter-face))) - (qualified_name (name) @font-lock-constant-face) - ,@(when (php-ts-mode--test-relative-name-p) - '((relative_name (name) @font-lock-constant-face)))) - - :language 'php - :feature 'property - '((enum_case - name: (_) @font-lock-type-face)) - - :language 'php - :feature 'attribute - '((((attribute (_) @attribute_name) @font-lock-preprocessor-face) - (:equal "Deprecated" @attribute_name)) - (attribute_group (attribute (name) @font-lock-constant-face))) - - :language 'php - :feature 'bracket - '((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face) - - :language 'php - :feature 'error - :override t - '((ERROR) @php-ts-mode--fontify-error))))) + "Return tree-sitter font-lock settings for `php-ts-mode'." + (treesit-font-lock-rules + + :language 'php + :feature 'keyword + :override t + `([,@(php-ts-mode--keywords)] @font-lock-keyword-face + ,@(when (php-ts-mode--test-visibility-modifier-operation-p) + '((visibility_modifier (operation) @font-lock-builtin-face))) + (var_modifier) @font-lock-builtin-face) + + :language 'php + :feature 'comment + :override t + '((comment) @font-lock-comment-face) + + :language 'php + :feature 'constant + `((boolean) @font-lock-constant-face + (null) @font-lock-constant-face + ;; predefined constant or built in constant (part of PHP core) + ((name) @font-lock-builtin-face + (:match ,(rx-to-string + `(: bos (or ,@php-ts-mode--predefined-constant) eos)) + @font-lock-builtin-face)) + ;; user defined constant + ((name) @font-lock-constant-face + (:match "\\`_*[A-Z][0-9A-Z_]+\\'" @font-lock-constant-face)) + (const_declaration + (const_element (name) @font-lock-constant-face)) + ;; declare directive + (declare_directive ["strict_types" "encoding" "ticks"] @font-lock-constant-face)) + + :language 'php + :feature 'name + '((goto_statement (name) @font-lock-constant-face) + (named_label_statement (name) @font-lock-constant-face)) + + :language 'php + :feature 'delimiter + `((["," ":" ";" "\\"]) @font-lock-delimiter-face) + + :language 'php + :feature 'operator + `((error_suppression_expression "@" @font-lock-keyword-face) + [,@(php-ts-mode--operators)] @font-lock-operator-face) + + :language 'php + :feature 'variable-name + :override t + '(((name) @font-lock-keyword-face (:equal "this" @font-lock-keyword-face)) + (variable_name (name) @font-lock-variable-name-face) + (relative_scope ["parent" "self" "static"] @font-lock-builtin-face) + (relative_scope) @font-lock-constant-face + (dynamic_variable_name (name) @font-lock-variable-name-face) + (member_access_expression + name: (_) @font-lock-variable-name-face) + (scoped_property_access_expression + scope: (name) @font-lock-constant-face) + (nullsafe_member_access_expression (name) @font-lock-variable-name-face) + (error_suppression_expression (name) @font-lock-property-name-face)) + + :language 'php + :feature 'string + `(("\"") @font-lock-string-face + (encapsed_string) @font-lock-string-face + (string_content) @font-lock-string-face + (string) @font-lock-string-face) + + :language 'php + :feature 'literal + '((integer) @font-lock-number-face + (float) @font-lock-number-face + (heredoc identifier: (heredoc_start) @font-lock-constant-face) + (heredoc_body (string_content) @font-lock-string-face) + (heredoc end_tag: (heredoc_end) @font-lock-constant-face) + (nowdoc identifier: (heredoc_start) @font-lock-constant-face) + (nowdoc_body (nowdoc_string) @font-lock-string-face) + (nowdoc end_tag: (heredoc_end) @font-lock-constant-face) + (shell_command_expression) @font-lock-string-face) + + :language 'php + :feature 'type + :override t + '((union_type "|" @font-lock-operator-face) + (union_type) @font-lock-type-face + (bottom_type) @font-lock-type-face + (primitive_type) @font-lock-type-face + ((primitive_type) @font-lock-keyword-face + (:equal "callable" @font-lock-keyword-face)) + (cast_type) @font-lock-type-face + (named_type) @font-lock-type-face + (optional_type) @font-lock-type-face) + + :language 'php + :feature 'definition + :override t + `((php_tag) @font-lock-preprocessor-face + ,@(if (php-ts-mode--test-php-end-tag-p) + '((php_end_tag) @font-lock-preprocessor-face) + '(("?>") @font-lock-preprocessor-face)) + ;; Highlights identifiers in declarations. + (class_declaration + name: (_) @font-lock-type-face) + (class_interface_clause (name) @font-lock-type-face) + (interface_declaration + name: (_) @font-lock-type-face) + (trait_declaration + name: (_) @font-lock-type-face) + (enum_declaration + name: (_) @font-lock-type-face) + (function_definition + name: (_) @font-lock-function-name-face) + ,@(when (php-ts-mode--test-property-hook-p) + '((property_hook (name) @font-lock-function-name-face))) + (method_declaration + name: (_) @font-lock-function-name-face) + (method_declaration + name: (name) @font-lock-builtin-face + (:match ,(rx-to-string + `(: bos (or ,@php-ts-mode--class-magic-methods) eos)) + @font-lock-builtin-face)) + ("=>") @font-lock-keyword-face + (object_creation_expression + (name) @font-lock-type-face) + ,@(when (php-ts-mode--test-namespace-name-as-prefix-p) + '((namespace_name_as_prefix "\\" @font-lock-delimiter-face) + (namespace_name_as_prefix + (namespace_name (name)) @font-lock-type-face))) + ,@(if (php-ts-mode--test-namespace-aliasing-clause-p) + '((namespace_aliasing_clause (name) @font-lock-type-face)) + '((namespace_use_clause alias: (name) @font-lock-type-face))) + ,@(when (not (php-ts-mode--test-namespace-use-group-clause-p)) + '((namespace_use_group + (namespace_use_clause (name) @font-lock-type-face)))) + (namespace_use_clause (name) @font-lock-type-face) + (namespace_name "\\" @font-lock-delimiter-face) + (namespace_name (name) @font-lock-type-face) + (use_declaration (name) @font-lock-property-use-face) + (use_instead_of_clause (name) @font-lock-type-face) + (binary_expression + operator: "instanceof" + right: (name) @font-lock-type-face)) + + :language 'php + :feature 'function-scope + :override t + '((scoped_call_expression + scope: (name) @font-lock-constant-face) + (class_constant_access_expression (name) @font-lock-constant-face)) + + :language 'php + :feature 'function-call + :override t + '((function_call_expression + function: (name) @font-lock-function-call-face) + (scoped_call_expression + name: (name) @font-lock-function-call-face) + (member_call_expression + name: (name) @font-lock-function-call-face) + (nullsafe_member_call_expression + name: (_) @font-lock-function-call-face)) + + :language 'php + :feature 'argument + '((argument + name: (_) @font-lock-constant-face)) + + :language 'php + :feature 'escape-sequence + :override t + '((string (escape_sequence) @font-lock-escape-face) + (encapsed_string (escape_sequence) @font-lock-escape-face) + (heredoc_body (escape_sequence) @font-lock-escape-face)) + + :language 'php + :feature 'base-clause + :override t + `((base_clause (name) @font-lock-type-face) + (use_as_clause (name) @font-lock-property-use-face) + ,@(when (not (php-ts-mode--test-namespace-name-as-prefix-p)) + '((qualified_name prefix: "\\" @font-lock-delimiter-face))) + (qualified_name (name) @font-lock-constant-face) + ,@(when (php-ts-mode--test-relative-name-p) + '((relative_name (name) @font-lock-constant-face)))) + + :language 'php + :feature 'property + '((enum_case + name: (_) @font-lock-type-face)) + + :language 'php + :feature 'attribute + '((((attribute (_) @attribute_name) @font-lock-preprocessor-face) + (:equal "Deprecated" @attribute_name)) + (attribute_group (attribute (name) @font-lock-constant-face))) + + :language 'php + :feature 'bracket + '((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face) + + :language 'php + :feature 'error + :override t + '((ERROR) @php-ts-mode--fontify-error))) ;;; Font-lock helpers -(defvar php-ts-mode--custom-html-font-lock-settings-cached nil - "Cached tree-sitter font-lock settings for HTML when embedded in PHP.") - (defun php-ts-mode--custom-html-font-lock-settings () "Tree-sitter Font-lock settings for HTML when embedded in PHP. -Like `mhtml-ts-mode--font-lock-settings' but adapted for `php-ts-mode'. - -Tree-sitter font-lock settings are evaluated the first time this -function is called. Subsequent calls return the first evaluated value." - (or php-ts-mode--custom-html-font-lock-settings-cached - (setq php-ts-mode--custom-html-font-lock-settings-cached - (treesit-replace-font-lock-feature-settings - (treesit-font-lock-rules - :language 'html - :override t - :feature 'comment - '((comment) @font-lock-comment-face - ;; handle shebang path and others type of comment - (document (text) @font-lock-comment-face))) - (mhtml-ts-mode--treesit-font-lock-settings))))) +Like `mhtml-ts-mode--font-lock-settings' but adapted for `php-ts-mode'." + (treesit-replace-font-lock-feature-settings + (treesit-font-lock-rules + :language 'html + :override t + :feature 'comment + '((comment) @font-lock-comment-face + ;; handle shebang path and others type of comment + (document (text) @font-lock-comment-face))) + (mhtml-ts-mode--treesit-font-lock-settings))) (defvar php-ts-mode--phpdoc-font-lock-settings (treesit-font-lock-rules diff --git a/lisp/textmodes/mhtml-ts-mode.el b/lisp/textmodes/mhtml-ts-mode.el index b97555de2fc..fe68f17a6da 100644 --- a/lisp/textmodes/mhtml-ts-mode.el +++ b/lisp/textmodes/mhtml-ts-mode.el @@ -259,28 +259,20 @@ NODE and PARENT are ignored." css--treesit-font-lock-feature-list)) "Settings for `treesit-font-lock-feature-list'.") -(defvar mhtml-ts-mode--treesit-font-lock-settings-cached nil - "Cached tree-sitter font-lock settings for `mhtml-ts-mode'.") - (defun mhtml-ts-mode--treesit-font-lock-settings () - "Return tree-sitter font-lock settings for `mhtml-ts-mode'. - -Tree-sitter font-lock settings are evaluated the first time this -function is called. Subsequent calls return the first evaluated value." - (or mhtml-ts-mode--treesit-font-lock-settings-cached - (setq mhtml-ts-mode--treesit-font-lock-settings-cached - (append html-ts-mode--font-lock-settings - (js--treesit-font-lock-settings) - ;; Let's replace a css rule with a new one that adds - ;; color to the css value. - (treesit-replace-font-lock-feature-settings - (treesit-font-lock-rules - :language 'css - :override t - :feature 'variable - '((plain_value) @mhtml-ts-mode--colorize-css-value - (color_value) @mhtml-ts-mode--colorize-css-value)) - css--treesit-settings))))) + "Return tree-sitter font-lock settings for `mhtml-ts-mode'." + (append html-ts-mode--font-lock-settings + (js--treesit-font-lock-settings) + ;; Let's replace a css rule with a new one that adds + ;; color to the css value. + (treesit-replace-font-lock-feature-settings + (treesit-font-lock-rules + :language 'css + :override t + :feature 'variable + '((plain_value) @mhtml-ts-mode--colorize-css-value + (color_value) @mhtml-ts-mode--colorize-css-value)) + css--treesit-settings))) (defvar mhtml-ts-mode--treesit-thing-settings ;; In addition to putting together the various definitions, we need to @@ -300,34 +292,26 @@ function is called. Subsequent calls return the first evaluated value." `((defun ,css--treesit-defun-type-regexp)))) "Settings for `treesit-thing-settings'.") -(defvar mhtml-ts-mode--treesit-indent-rules-cached nil - "Cached tree-sitter indent rules for `mhtml-ts-mode'.") - (defun mhtml-ts-mode--treesit-indent-rules () - "Return tree-sitter indent rules for `mhtml-ts-mode'. - -Tree-sitter indent rules are evaluated the first time this function -is called. Subsequent calls return the first evaluated value." - (or mhtml-ts-mode--treesit-indent-rules-cached - (setq mhtml-ts-mode--treesit-indent-rules-cached - (treesit--indent-rules-optimize - (append html-ts-mode--indent-rules - ;; Extended rules for js and css, to indent - ;; appropriately when injected into html - (treesit-simple-indent-modify-rules - 'javascript - `((javascript ((parent-is "program") - mhtml-ts-mode--js-css-tag-bol - mhtml-ts-mode--js-css-indent-offset))) - (js--treesit-indent-rules) - :replace) - (treesit-simple-indent-modify-rules - 'css - `((css ((parent-is "stylesheet") - mhtml-ts-mode--js-css-tag-bol - mhtml-ts-mode--js-css-indent-offset))) - css--treesit-indent-rules - :prepend)))))) + "Return tree-sitter indent rules for `mhtml-ts-mode'." + (treesit--indent-rules-optimize + (append html-ts-mode--indent-rules + ;; Extended rules for js and css, to indent + ;; appropriately when injected into html + (treesit-simple-indent-modify-rules + 'javascript + `((javascript ((parent-is "program") + mhtml-ts-mode--js-css-tag-bol + mhtml-ts-mode--js-css-indent-offset))) + (js--treesit-indent-rules) + :replace) + (treesit-simple-indent-modify-rules + 'css + `((css ((parent-is "stylesheet") + mhtml-ts-mode--js-css-tag-bol + mhtml-ts-mode--js-css-indent-offset))) + css--treesit-indent-rules + :prepend)))) (defvar mhtml-ts-mode--treesit-aggregated-simple-imenu-settings `((html ,@html-ts-mode--treesit-simple-imenu-settings)