summaryrefslogtreecommitdiff
path: root/lisp/progmodes
diff options
context:
space:
mode:
authorPo Lu <luangruo@yahoo.com>2023-04-28 11:51:01 +0800
committerPo Lu <luangruo@yahoo.com>2023-04-28 11:51:01 +0800
commita97c382682b483b34351c6757e54e3139fa27f89 (patch)
tree497b1b52dd955f593a0a39f1032e3fd55aabcc35 /lisp/progmodes
parentdbd74657908e67e2a5001fbe440fb2a01a2f01af (diff)
parenta40f18162377d2f9a78d7443fd0f8106c8c28c3d (diff)
downloademacs-a97c382682b483b34351c6757e54e3139fa27f89.tar.gz
Merge from origin/emacs-29
a40f1816237 Fix two crashes upon startup 44ebd9cbd56 Eglot: explain how to update Eglot in manual (bug#62720) 941ef044f2e Eglot: fix edge case when deleting inlay hint overlays a365984d9e1 package-upgrade[-all]: Expand docstrings to note the curr... f965f35b33b Rename all functions called package-*-update-* to package... 31b58161bb5 Fix FOR_EACH_TAIL in c-ts-mode (bug#62951) 0cf6e0998ba * Makefile.in (distclean): Remove the 'native-lisp' direc... 933705d61e5 Improve greek-ibycus4 input method # Conflicts: # etc/NEWS
Diffstat (limited to 'lisp/progmodes')
-rw-r--r--lisp/progmodes/c-ts-mode.el99
-rw-r--r--lisp/progmodes/eglot.el27
2 files changed, 121 insertions, 5 deletions
diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
index bbc7979667c..353100ca1ce 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -357,7 +357,9 @@ PARENT, BOL, ARGS are the same as other anchor functions."
"Indent rules supported by `c-ts-mode'.
MODE is either `c' or `cpp'."
(let ((common
- `(((parent-is "translation_unit") column-0 0)
+ `((c-ts-mode--for-each-tail-body-matcher prev-line c-ts-mode-indent-offset)
+
+ ((parent-is "translation_unit") column-0 0)
((query "(ERROR (ERROR)) @indent") column-0 0)
((node-is ")") parent 1)
((node-is "]") parent-bol 0)
@@ -969,6 +971,81 @@ if `c-ts-mode-emacs-sources-support' is non-nil."
(or (treesit-add-log-current-defun)
(c-ts-mode--defun-name (c-ts-mode--emacs-defun-at-point))))
+;;; FOR_EACH_TAIL fix
+;;
+;; FOR_EACH_TAIL (and FOR_EACH_TAIL_SAFE) followed by a unbracketed
+;; body will mess up the parser, which parses the thing as a function
+;; declaration. We "fix" it by adding a shadow parser, emacs-c (which
+;; is just c but under a different name). We use emacs-c to find each
+;; FOR_EACH_TAIL with a unbracketed body, and set the ranges of the C
+;; parser so that it skips those FOR_EACH_TAIL's. Note that we only
+;; ignore FOR_EACH_TAIL's with a unbracketed body. Those with a
+;; bracketed body parses more or less fine.
+
+(defvar c-ts-mode--for-each-tail-regexp
+ (rx "FOR_EACH_" (or "TAIL" "TAIL_SAFE" "ALIST_VALUE"
+ "LIVE_BUFFER" "FRAME"))
+ "A regexp matching all the FOR_EACH_TAIL variants.")
+
+(defun c-ts-mode--for-each-tail-body-matcher (_n _p bol &rest _)
+ "A matcher that matches the first line after a FOR_EACH_TAIL.
+For BOL see `treesit-simple-indent-rules'."
+ (when c-ts-mode-emacs-sources-support
+ (save-excursion
+ (goto-char bol)
+ (forward-line -1)
+ (skip-chars-forward " \t")
+ (looking-at c-ts-mode--for-each-tail-regexp))))
+
+(defvar c-ts-mode--emacs-c-range-query
+ (treesit-query-compile
+ 'emacs-c `(((declaration
+ type: (macro_type_specifier
+ name: (identifier) @_name)
+ @for-each-tail)
+ (:match ,c-ts-mode--for-each-tail-regexp
+ @_name))))
+ "Query that finds the FOR_EACH_TAIL with a unbracketed body.")
+
+(defvar-local c-ts-mode--for-each-tail-ranges nil
+ "Ranges covering all the FOR_EACH_TAIL's in the buffer.")
+
+(defun c-ts-mode--reverse-ranges (ranges beg end)
+ "Reverse RANGES and return the new ranges between BEG and END.
+Positions that were included RANGES are not in the returned
+ranges, and vice versa.
+
+Return nil if RANGES is nil. This way, passing the returned
+ranges to `treesit-parser-set-included-ranges' will make the
+parser parse the whole buffer."
+ (if (null ranges)
+ nil
+ (let ((new-ranges nil)
+ (prev-end beg))
+ (dolist (range ranges)
+ (when (< prev-end (car range))
+ (push (cons prev-end (car range)) new-ranges))
+ (setq prev-end (cdr range)))
+ (when (< prev-end end)
+ (push (cons prev-end end) new-ranges))
+ (nreverse new-ranges))))
+
+(defun c-ts-mode--emacs-set-ranges (beg end)
+ "Set ranges for the C parser to skip some FOR_EACH_TAIL's.
+BEG and END are described in `treesit-range-rules'."
+ (let* ((c-parser (treesit-parser-create 'c))
+ (old-ranges c-ts-mode--for-each-tail-ranges)
+ (new-ranges (treesit-query-range
+ 'emacs-c c-ts-mode--emacs-c-range-query beg end))
+ (set-ranges (treesit--clip-ranges
+ (treesit--merge-ranges
+ old-ranges new-ranges beg end)
+ (point-min) (point-max)))
+ (reversed-ranges (c-ts-mode--reverse-ranges
+ set-ranges (point-min) (point-max))))
+ (setq-local c-ts-mode--for-each-tail-ranges set-ranges)
+ (treesit-parser-set-included-ranges c-parser reversed-ranges)))
+
;;; Modes
(defvar-keymap c-ts-base-mode-map
@@ -1101,6 +1178,17 @@ in your configuration."
:after-hook (c-ts-mode-set-modeline)
(when (treesit-ready-p 'c)
+ ;; Add a fake "emacs-c" language which is just C. Used for
+ ;; skipping FOR_EACH_TAIL, see `c-ts-mode--emacs-set-ranges'.
+ (setf (alist-get 'emacs-c treesit-load-name-override-list)
+ '("libtree-sitter-c" "tree_sitter_c"))
+ ;; If Emacs source support is enabled, make sure emacs-c parser is
+ ;; after c parser in the parser list. This way various tree-sitter
+ ;; functions will automatically use the c parser rather than the
+ ;; emacs-c parser.
+ (when c-ts-mode-emacs-sources-support
+ (treesit-parser-create 'emacs-c))
+
(treesit-parser-create 'c)
;; Comments.
(setq-local comment-start "/* ")
@@ -1114,9 +1202,16 @@ in your configuration."
(setq-local treesit-defun-tactic 'top-level)
(treesit-major-mode-setup)
+ ;; Emacs source support: handle DEFUN and FOR_EACH_TAIL gracefully.
(when c-ts-mode-emacs-sources-support
(setq-local add-log-current-defun-function
- #'c-ts-mode--emacs-current-defun-name))))
+ #'c-ts-mode--emacs-current-defun-name)
+
+ (setq-local treesit-range-settings
+ (treesit-range-rules 'c-ts-mode--emacs-set-ranges))
+
+ (setq-local treesit-language-at-point-function
+ (lambda (_pos) 'c)))))
;;;###autoload
(define-derived-mode c++-ts-mode c-ts-base-mode "C++"
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 3134d55fc08..6ae27a13845 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -2002,6 +2002,16 @@ If it is activated, also signal textDocument/didOpen."
(declare (obsolete info "1.10"))
(interactive) (info "(eglot)"))
+;;;###autoload
+(defun eglot-update (&rest _) "Update Eglot."
+ (interactive)
+ (with-no-warnings
+ (require 'package)
+ (unless package-archive-contents (package-refresh-contents))
+ (when-let ((existing (cadr (assoc 'eglot package-alist))))
+ (package-delete existing t))
+ (package-install (cadr (assoc 'eglot package-archive-contents)))))
+
(easy-menu-define eglot-menu nil "Eglot"
`("Eglot"
;; Commands for getting information and customization.
@@ -3129,8 +3139,7 @@ for which LSP on-type-formatting should be requested."
(funcall snippet-fn (or insertText label))))
(when (cl-plusp (length additionalTextEdits))
(eglot--apply-text-edits additionalTextEdits)))
- (eglot--signal-textDocument/didChange)
- (eldoc)))))))))
+ (eglot--signal-textDocument/didChange)))))))))
(defun eglot--hover-info (contents &optional _range)
(mapconcat #'eglot--format-markup
@@ -3743,7 +3752,19 @@ If NOERROR, return predicate, else erroring function."
:success-fn (lambda (hints)
(eglot--when-live-buffer buf
(eglot--widening
- (remove-overlays from to 'eglot--inlay-hint t)
+ ;; Overlays ending right at FROM with an
+ ;; `after-string' property logically belong to
+ ;; the (FROM TO) region. Likewise, such
+ ;; overlays ending at TO don't logically belong
+ ;; to it.
+ (dolist (o (overlays-in (1- from) to))
+ (when (and (overlay-get o 'eglot--inlay-hint)
+ (cond ((eq (overlay-end o) from)
+ (overlay-get o 'after-string))
+ ((eq (overlay-end o) to)
+ (overlay-get o 'before-string))
+ (t)))
+ (delete-overlay o)))
(mapc paint-hint hints))))
:deferred 'eglot--update-hints-1)))