nim-lang / nim-mode

An emacs major mode for the Nim programming language
137 stars 46 forks source link

Nimsuggest's find definition freezes Emacs #243

Open dunrix opened 2 years ago

dunrix commented 2 years ago
  1. Create basic nim project with nimble init. Source code of main project's file "playground.nim" follows.
# This is just an example to get you started. A typical hybrid package
# uses this file as the main entry point of the application.

import playgroundpkg/submodule

when isMainModule:
  echo(submodule.getWelcomeMessage())
  1. Open in Emacs with nim-mode and nimsuggest-mode enabled
  2. Place cursor on some keyword or module-identifier and invoke xref-find-definitions (M-. shortcut by default)
  3. Emacs hangs forever, eating 100% of CPU time. Can be interrupted with (C-g).

When invoked on procs, variables, constants, types - it works as expected, ie. brings in source for that particular identifier.

Command line version of nimsuggest in comparison does work properly, and for keywords & module names instantly replies with '.' (dot symbol), like no definition has been found. Related issue report https://github.com/PMunch/nimlsp/issues/128

woolsweater commented 1 year ago

I have also run into this. I am not familiar with the expected behavior for the IPC with nimsuggest, so I may be wrong, but it looks to me like the while loop inside nimsuggest--call-sync has a slightly flawed termination condition. Here's one possible resolution:

diff --git a/nim-suggest.el b/nim-suggest.el
index 3730ab6..276d66c 100644
--- a/nim-suggest.el
+++ b/nim-suggest.el
@@ -295,11 +295,13 @@ The CALLBACK function is called when it got the response."
      (lambda (candidates)
        (when (eq (current-buffer) buf)
          (setq res (funcall callback candidates)))))
-    (while (and (eq 'trash res) (eq (current-buffer) buf))
-      (if (> (- (time-to-seconds) start) 2)
-          (nim-log "EPC-sync(%s): timeout %d sec" (symbol-name method) 2)
-        (sleep-for 0.03)))
-    (unless (eq 'trash res)
+    (while (and (eq 'trash res)
+                (eq (current-buffer) buf))
+      (if (< (- (time-to-seconds) start) 2)
+          (sleep-for 0.03)
+        (nim-log "EPC-sync(%s): timeout %d sec" (symbol-name method) 2)
+        (setq res 'timeout)))
+    (unless (or (eq 'trash res) (eq 'timeout res))
       res)))

 (defun nimsuggest--get-dirty-dir ()

This ensures the loop terminates when the timeout duration is reached.

I can put up a PR if this looks like a reasonable solution to the maintainers.