diff options
author | Po Lu <luangruo@yahoo.com> | 2023-04-28 11:51:01 +0800 |
---|---|---|
committer | Po Lu <luangruo@yahoo.com> | 2023-04-28 11:51:01 +0800 |
commit | a97c382682b483b34351c6757e54e3139fa27f89 (patch) | |
tree | 497b1b52dd955f593a0a39f1032e3fd55aabcc35 /lisp/progmodes | |
parent | dbd74657908e67e2a5001fbe440fb2a01a2f01af (diff) | |
parent | a40f18162377d2f9a78d7443fd0f8106c8c28c3d (diff) | |
download | emacs-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.el | 99 | ||||
-rw-r--r-- | lisp/progmodes/eglot.el | 27 |
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))) |