From 9d8e1af6f41bf11704c00cc5f6fdbc5eb52ed2d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20T=C3=A1vora?= Date: Wed, 21 Jan 2026 11:57:38 +0000 Subject: [PATCH] Eglot: fix textDocument/prepareRename support Can't send prepareRename requests willy-nilly. See https://github.com/joaotavora/eglot/issues/1554. * lisp/progmodes/eglot.el (eglot--rename-interactive): Fix. (eglot-client-capabilities): Advertise "prepareSupport". * etc/EGLOT-NEWS: Mention change. --- etc/EGLOT-NEWS | 5 +++++ lisp/progmodes/eglot.el | 38 +++++++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/etc/EGLOT-NEWS b/etc/EGLOT-NEWS index 201d782575f..89870c64641 100644 --- a/etc/EGLOT-NEWS +++ b/etc/EGLOT-NEWS @@ -50,6 +50,11 @@ beneficial and helps servers avoid costly useless work. Eglot now sets 'imenu-create-index-function' directly without using advice, making the integration cleaner and more predictable. +** Fixed textDocument/prepareRename support (github#1554) + +Eglot now properly checks server capabilities before sending +prepareRename requests. + * Changes in Eglot 1.21 (11/1/2026) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 10a58c57989..b418bfdedc8 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -1139,7 +1139,8 @@ object." :isPreferredSupport t) :formatting `(:dynamicRegistration :json-false) :rangeFormatting `(:dynamicRegistration :json-false) - :rename `(:dynamicRegistration :json-false) + :rename `(:dynamicRegistration :json-false + :prepareSupport t) :semanticTokens `(:dynamicRegistration :json-false :requests (:full (:delta t)) :overlappingTokenSupport t @@ -4489,20 +4490,27 @@ the edit was attempted and optionally why not." (if (user-accepts-p) (apply-all) `(nil "decision to decline"))) ((apply-all))))))) -(cl-defun eglot--rename-interactive (&aux region) - (eglot-server-capable-or-lose :renameProvider) - (let* ((probe (eglot--request (eglot--current-server-or-lose) - :textDocument/prepareRename - (eglot--TextDocumentPositionParams))) - (def - (cond ((null probe) (user-error "[eglot] Can't rename here")) - ((plist-get probe :placeholder)) - ((plist-get probe :defaultBehavior) (thing-at-point 'symbol t)) - ((setq region (eglot-range-region probe)) - (buffer-substring-no-properties (car region) (cdr region)))))) - (list (read-from-minibuffer - (format "Rename `%s' to: " (or def "unknown symbol")) - nil nil nil nil def)))) +(cl-defun eglot--rename-interactive + (&aux + def region + (rename-support (eglot-server-capable-or-lose :renameProvider)) + (prepare-support (and (listp rename-support) + (plist-get rename-support :prepareProvider)))) + (setq + def + (cond (prepare-support + (let ((x (eglot--request (eglot--current-server-or-lose) + :textDocument/prepareRename + (eglot--TextDocumentPositionParams)))) + (cond ((null x) (user-error "[eglot] Can't rename here")) + ((plist-get x :placeholder)) + ((plist-get x :defaultBehavior) (thing-at-point 'symbol t)) + ((setq region (eglot-range-region x)) + (buffer-substring-no-properties (car region) (cdr region)))))) + (t (thing-at-point 'symbol t)))) + (list (read-from-minibuffer + (format "Rename `%s' to: " (or def "unknown symbol")) + nil nil nil nil def))) (defun eglot-rename (newname) "Rename the current symbol to NEWNAME."