From 141d700dfc7a795151280ac84046bf01229bf99f Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Sun, 5 May 2024 17:23:29 -0400 Subject: [PATCH] eglot.el: Require `track-changes` unconditionally * lisp/emacs-lisp/track-changes.el: Bump version. * lisp/progmodes/eglot.el: Require `track-changes` unconditionally. (Package-Requires:): Add `track-changes`. (eglot--track-changes-signal, eglot--signal-textDocument/didChange) (eglot--managed-mode): Remove non-track-changes alternative code. (eglot--before-change, eglot--after-change): Delete functions. (cherry picked from commit 44036b32ba398cf284320285b60be35874b6eea5) --- lisp/emacs-lisp/track-changes.el | 8 ++- lisp/progmodes/eglot.el | 94 ++++++-------------------------- 2 files changed, 25 insertions(+), 77 deletions(-) diff --git a/lisp/emacs-lisp/track-changes.el b/lisp/emacs-lisp/track-changes.el index 6562cc7eb86..2824a70586d 100644 --- a/lisp/emacs-lisp/track-changes.el +++ b/lisp/emacs-lisp/track-changes.el @@ -3,7 +3,7 @@ ;; Copyright (C) 2024 Free Software Foundation, Inc. ;; Author: Stefan Monnier -;; Version: 1.1 +;; Version: 1.2 ;; Package-Requires: ((emacs "24")) ;; This file is part of GNU Emacs. @@ -74,6 +74,12 @@ ;; id (lambda (beg end before) ;; ..DO THE THING..)))))))) +;;; News: + +;; Since v1.1: +;; +;; - New function `track-changes-inconsistent-state-p'. + ;;; Code: ;; Random ideas: diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 89e0aa63db4..1f2e31ed0fa 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -7,7 +7,7 @@ ;; Maintainer: João Távora ;; URL: https://github.com/joaotavora/eglot ;; Keywords: convenience, languages -;; Package-Requires: ((emacs "26.3") (jsonrpc "1.0.24") (flymake "1.2.1") (project "0.9.8") (xref "1.6.2") (eldoc "1.14.0") (seq "2.23") (external-completion "0.1") (compat "27.1")) +;; Package-Requires: ((emacs "26.3") (compat "27.1") (eldoc "1.14.0") (external-completion "0.1") (flymake "1.2.1") (jsonrpc "1.0.24") (project "0.9.8") (seq "2.23") (track-changes "1.2") (xref "1.6.2")) ;; This is a GNU ELPA :core package. Avoid adding functionality ;; that is not available in the version of Emacs recorded above or any @@ -103,14 +103,12 @@ (require 'pcase) (require 'compile) ; for some faces (require 'warnings) -(eval-when-compile - (require 'subr-x)) (require 'filenotify) (require 'ert) (require 'text-property-search nil t) (require 'diff-mode) (require 'diff) -(require 'track-changes nil t) +(require 'track-changes) (require 'compat) ;; These dependencies are also GNU ELPA core packages. Because of @@ -135,6 +133,10 @@ ;; the loaded file is not the one that should have been loaded. (mapc reload '(project flymake xref jsonrpc external-completion)))) +;; Keep the eval-when-compile requires at the end, in case it's already been +;; required unconditionally by some earlier `require'. +(eval-when-compile (require 'subr-x)) + ;; forward-declare, but don't require (Emacs 28 doesn't seem to care) (defvar markdown-fontify-code-blocks-natively) (defvar company-backends) @@ -1983,13 +1985,10 @@ Use `eglot-managed-p' to determine if current buffer is managed.") ("utf-8" (eglot--setq-saving eglot-current-linepos-function #'eglot-utf-8-linepos) (eglot--setq-saving eglot-move-to-linepos-function #'eglot-move-to-utf-8-linepos))) - (if (fboundp 'track-changes-register) - (unless eglot--track-changes - (setq eglot--track-changes - (track-changes-register - #'eglot--track-changes-signal :disjoint t))) - (add-hook 'after-change-functions #'eglot--after-change nil t) - (add-hook 'before-change-functions #'eglot--before-change nil t)) + (unless eglot--track-changes + (setq eglot--track-changes + (track-changes-register + #'eglot--track-changes-signal :disjoint t))) (add-hook 'kill-buffer-hook #'eglot--managed-mode-off nil t) ;; Prepend "didClose" to the hook after the "nonoff", so it will run first (add-hook 'kill-buffer-hook #'eglot--signal-textDocument/didClose nil t) @@ -2027,8 +2026,6 @@ Use `eglot-managed-p' to determine if current buffer is managed.") (when eglot--track-changes (track-changes-unregister eglot--track-changes) (setq eglot--track-changes nil)) - (remove-hook 'after-change-functions #'eglot--after-change t) - (remove-hook 'before-change-functions #'eglot--before-change t) (remove-hook 'kill-buffer-hook #'eglot--managed-mode-off t) (remove-hook 'kill-buffer-hook #'eglot--signal-textDocument/didClose t) (remove-hook 'before-revert-hook #'eglot--signal-textDocument/didClose t) @@ -2608,7 +2605,7 @@ buffer." `(:triggerKind 2 :triggerCharacter ,trigger) `(:triggerKind 1))))) (defvar-local eglot--recent-changes nil - "Recent buffer changes as collected by `eglot--before-change'.") + "Recent buffer changes as collected by `eglot--track-changes-fetch'.") (cl-defmethod jsonrpc-connection-ready-p ((_server eglot-lsp-server) _what) "Tell if SERVER is ready for WHAT in current buffer." @@ -2616,56 +2613,9 @@ buffer." (defvar-local eglot--change-idle-timer nil "Idle timer for didChange signals.") -(defun eglot--before-change (beg end) - "Hook onto `before-change-functions' with BEG and END." - (when (listp eglot--recent-changes) - ;; Records BEG and END, crucially convert them into LSP - ;; (line/char) positions before that information is lost (because - ;; the after-change thingy doesn't know if newlines were - ;; deleted/added). Also record markers of BEG and END - ;; (github#259) - (push `(,(eglot--pos-to-lsp-position beg) - ,(eglot--pos-to-lsp-position end) - (,beg . ,(copy-marker beg nil)) - (,end . ,(copy-marker end t))) - eglot--recent-changes))) - (defvar eglot--document-changed-hook '(eglot--signal-textDocument/didChange) "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) - (pcase (car-safe eglot--recent-changes) - (`(,lsp-beg ,lsp-end - (,b-beg . ,b-beg-marker) - (,b-end . ,b-end-marker)) - ;; github#259 and github#367: with `capitalize-word' & friends, - ;; `before-change-functions' records the whole word's `b-beg' and - ;; `b-end'. Similarly, when `fill-paragraph' coalesces two - ;; lines, `b-beg' and `b-end' mark end of first line and end of - ;; second line, resp. In both situations, `beg' and `end' - ;; received here seemingly contradict that: they will differ by 1 - ;; and encompass the capitalized character or, in the coalescing - ;; case, the replacement of the newline with a space. We keep - ;; both markers and positions to detect and correct this. In - ;; this specific case, we ignore `beg', `len' and - ;; `pre-change-len' and send richer information about the region - ;; from the markers. I've also experimented with doing this - ;; unconditionally but it seems to break when newlines are added. - (if (and (= b-end b-end-marker) (= b-beg b-beg-marker) - (or (/= beg b-beg) (/= end b-end))) - (setcar eglot--recent-changes - `(,lsp-beg ,lsp-end ,(- b-end-marker b-beg-marker) - ,(buffer-substring-no-properties b-beg-marker - b-end-marker))) - (setcar eglot--recent-changes - `(,lsp-beg ,lsp-end ,pre-change-length - ,(buffer-substring-no-properties beg end))))) - (_ (setf eglot--recent-changes :emacs-messup))) - (eglot--track-changes-signal nil)) - (defun eglot--track-changes-fetch (id) (if (eq eglot--recent-changes :pending) (setq eglot--recent-changes nil)) (track-changes-fetch @@ -2705,15 +2655,14 @@ Records BEG, END and PRE-CHANGE-LENGTH locally." (lambda (buf) (eglot--when-live-buffer buf (when eglot--managed-mode - (if (and (fboundp 'track-changes-inconsistent-state-p) - (track-changes-inconsistent-state-p)) + (if (track-changes-inconsistent-state-p) ;; Not a good time (e.g. in the middle of Quail thingy, ;; bug#70541): reschedule for the next idle period. - (eglot--add-one-shot-hook - 'post-command-hook - (lambda () - (eglot--when-live-buffer buf - (eglot--track-changes-signal id)))) + (eglot--add-one-shot-hook + 'post-command-hook + (lambda () + (eglot--when-live-buffer buf + (eglot--track-changes-signal id)))) (run-hooks 'eglot--document-changed-hook) (setq eglot--change-idle-timer nil))))) (current-buffer)))) @@ -2820,8 +2769,7 @@ When called interactively, use the currently active server" (defun eglot--signal-textDocument/didChange () "Send textDocument/didChange to server." - (when eglot--track-changes - (eglot--track-changes-fetch eglot--track-changes)) + (eglot--track-changes-fetch eglot--track-changes) (when eglot--recent-changes (let* ((server (eglot--current-server-or-lose)) (sync-capability (eglot-server-capable :textDocumentSync)) @@ -2839,12 +2787,6 @@ When called interactively, use the currently active server" (buffer-substring-no-properties (point-min) (point-max))))) (cl-loop for (beg end len text) in (reverse eglot--recent-changes) - ;; github#259: `capitalize-word' and commands based - ;; on `casify_region' will cause multiple duplicate - ;; empty entries in `eglot--before-change' calls - ;; without an `eglot--after-change' reciprocal. - ;; Weed them out here. - when (numberp len) ;FIXME: Not needed with `track-changes'. vconcat `[,(list :range `(:start ,beg :end ,end) :rangeLength len :text text)])))) (setq eglot--recent-changes nil) -- 2.39.5