[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#927749: elpa-lsp-mode: Race condition in lsp-mode when using imenu and session restore

Package: elpa-lsp-mode
Version: 6.0-1
Severity: normal
Tags: patch

There is a race condition possibly leading to an error when lsp-mode is used
with imenu and session restore. See the upstream bug report for more details:

The author helped in getting a patch for version 6.0 as included in Debian
Buster. The packet lsp-ui also need a small patch, I'll file another bug for

-- System Information:
Debian Release: buster/sid
  APT prefers testing
  APT policy: (990, 'testing'), (50, 'unstable')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 4.19.0-4-amd64 (SMP w/8 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE=en_US:us (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

Versions of packages elpa-lsp-mode depends on:
ii  elpa-dash             2.14.1+dfsg-1
ii  elpa-dash-functional  1.2.0+dfsg-5
ii  elpa-f                0.20.0-1
ii  elpa-ht               2.2-2
ii  elpa-spinner          1.7.3-1
ii  emacsen-common        3.0.4

Versions of packages elpa-lsp-mode recommends:
ii  emacs              1:26.1+1-3.2
ii  emacs-gtk [emacs]  1:26.1+1-3.2

elpa-lsp-mode suggests no packages.

-- no debconf information
--- lsp-mode.el.orig	2019-01-21 21:56:43.000000000 +0100
+++ lsp-mode.el	2019-04-22 19:17:56.714357377 +0200
@@ -473,6 +473,16 @@
 (defvar lsp--tcp-port 10000)
+(defvar-local lsp--document-symbols nil
+  "The latest document symbols.")
+(defvar-local lsp--document-symbols-request-async nil
+  "If non-nil, request document symbols asynchronously.")
+(defvar-local lsp--document-symbols-tick -1
+  "The value of `buffer-chars-modified-tick' when document
+  symbols were last retrieved.")
 (cl-defgeneric lsp-execute-command (server command arguments)
   "Ask SERVER to execute COMMAND with ARGUMENTS.")
@@ -1674,6 +1684,9 @@
          (kind (if (hash-table-p sync) (gethash "change" sync) sync)))
     (setq lsp--server-sync-method (or lsp-document-sync-method
                                       (alist-get kind lsp--sync-methods))))
+  (when (and lsp-auto-configure (lsp--capability "documentSymbolProvider"))
+    (lsp-enable-imenu))
   (run-hooks 'lsp-after-open-hook))
 (define-inline lsp--text-document-identifier ()
@@ -2591,8 +2604,38 @@
                 'def-params td-params)))
 (defun lsp--get-document-symbols ()
-  (lsp-request "textDocument/documentSymbol"
-               `(:textDocument ,(lsp--text-document-identifier))))
+  "Get document symbols.
+If the buffer has not been modified since symbols were last
+retrieved, simply return the latest result.
+Else, if the request was initiated by Imenu updating its menu-bar
+entry, perform it asynchronously; i.e., give Imenu the latest
+result and then force a refresh when a new one is available.
+Else (e.g., due to intereactive use of `imenu' or `xref'),
+perform the request synchronously."
+  (if (= (buffer-chars-modified-tick) lsp--document-symbols-tick)
+      lsp--document-symbols
+    (let ((method "textDocument/documentSymbol")
+	      (params `(:textDocument ,(lsp--text-document-identifier)))
+	      (tick (buffer-chars-modified-tick)))
+      (if (not lsp--document-symbols-request-async)
+	      (prog1
+	          (setq lsp--document-symbols (lsp-request method params))
+	        (setq lsp--document-symbols-tick tick))
+	    (lsp-request-async method params
+			               (lambda (document-symbols)
+			                 (setq lsp--document-symbols document-symbols
+				                   lsp--document-symbols-tick tick)
+			                 (lsp--imenu-refresh))
+			               :mode 'alive)
+	    lsp--document-symbols))))
+(advice-add 'imenu-update-menubar :around
+	        (lambda (oldfun &rest r)
+	          (let ((lsp--document-symbols-request-async t))
+		        (apply oldfun r))))
 (defun lsp--xref-backend () 'xref-lsp)
@@ -3173,9 +3216,14 @@
          (result (compare-strings name1 0 (length name1) name2 0 (length name2))))
     (if (numberp result) result 0)))
+(defun lsp--imenu-refresh ()
+  "Force Imenu to refresh itself."
+  (imenu--menubar-select imenu--rescan-item))
 (defun lsp-enable-imenu ()
   "Use lsp-imenu for the current buffer."
-  (setq-local imenu-create-index-function #'lsp--imenu-create-index))
+  (setq-local imenu-create-index-function #'lsp--imenu-create-index)
+  (lsp--imenu-refresh))
 (defun lsp-resolve-final-function (command)
   "Resolve final function COMMAND."
@@ -3304,8 +3352,6 @@
     (lsp-ui-flycheck-enable t)
     (flycheck-mode 1)))
-  (lsp-enable-imenu)
   (when (functionp 'company-lsp)
     (company-mode 1)
     (setq-local company-backends '(company-lsp))

Reply to: