nex3 / perspective-el

Perspectives for Emacs.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Perspective is killing lsp-servers when switching between perspective projects.

Randy1Burrell opened this issue · comments

Hello perspective team. I am having an issue with perspective.

What happened

I'm using lsp with rust and my lsp servers gets killed when I switch to another perspective project from a rust project where an lsp server is activated. I get this error log whenever I switch back to the project where I was using lsp and enable toggle-debug-on-error:

Debugger entered--Lisp error: (error "The connected server(s) does not support method te...")
  signal(error ("The connected server(s) does not support method te..."))
  error("The connected server(s) does not support method %s..." "textDocument/inlayHint")
  lsp--send-request-async((:jsonrpc "2.0" :method "textDocument/inlayHint" :params (:textDocument (:uri "file:///Users/.../Projects/.../Rust/...") :range (:start (:line 0 :character 0) :end (:line 4 :character 0)))) #f(compiled-function (res) #<bytecode 0x158222e6dd15ec31>) tick nil nil nil nil)
  lsp-request-async("textDocument/inlayHint" (:textDocument (:uri "file:///Users/.../Projects/.../Rust/...") :range (:start (:line 0 :character 0) :end (:line 4 :character 0))) #f(compiled-function (res) #<bytecode 0x158222e6dd15ec31>) :mode tick)
  lsp-update-inlay-hints(1 51)
  lsp--update-inlay-hints-scroll-function(#<window 31 on main.rs> 1)
  #<subr set-window-buffer>(nil #<buffer main.rs> nil)
  ad-Advice-set-window-buffer(#<subr set-window-buffer> nil #<buffer main.rs>)
  apply(ad-Advice-set-window-buffer #<subr set-window-buffer> (nil #<buffer main.rs>))
  set-window-buffer(nil #<buffer main.rs>)
  #f(compiled-function (buffer-or-name &optional norecord force-same-window) "Display buffer BUFFER-OR-NAME in the selected window.\n\nWARNING: This is NOT the way to work on another buffer temporarily\nwithin a Lisp program!  Use `set-buffer' instead.  That avoids\nmessing with the `window-buffer' correspondences.\n\nIf the selected window cannot display the specified buffer\nbecause it is a minibuffer window or strongly dedicated to\nanother buffer, call `pop-to-buffer' to select the buffer in\nanother window.  In interactive use, if the selected window is\nstrongly dedicated to its buffer, the value of the option\n`switch-to-buffer-in-dedicated-window' specifies how to proceed.\n\nIf called interactively, read the buffer name using `read-buffer'.\nThe variable `confirm-nonexistent-file-or-buffer' determines\nwhether to request confirmation before creating a new buffer.\nSee `read-buffer' for features related to input and completion\nof buffer names.\n\nBUFFER-OR-NAME may be a buffer, a string (a buffer name), or nil.\nIf BUFFER-OR-NAME is a string that does not identify an existing\nbuffer, create a buffer with that name.  If BUFFER-OR-NAME is\nnil, switch to the buffer returned by `other-buffer'.\n\nIf optional argument NORECORD is non-nil, do not put the buffer\nat the front of the buffer list, and do not make the window\ndisplaying it the most recently selected one.\n\nIf optional argument FORCE-SAME-WINDOW is non-nil, the buffer\nmust be displayed in the selected window when called\nnon-interactively; if that is impossible, signal an error rather\nthan calling `pop-to-buffer'.  It has no effect when the option\n`switch-to-buffer-obey-display-actions' is non-nil.\n\nThe option `switch-to-buffer-preserve-window-point' can be used\nto make the buffer appear at its last position in the selected\nwindow.\n\nIf the option `switch-to-buffer-obey-display-actions' is non-nil,\nrun the function `pop-to-buffer-same-window' instead.\nThis may display the buffer in another window as specified by\n`display-buffer-overriding-action', `display-buffer-alist' and\nother display related variables.  If this results in displaying\nthe buffer in the selected window, window start and point are adjusted\nas prescribed by the option `switch-to-buffer-preserve-window-point'.\nOtherwise, these are left alone.\n\nReturn the buffer switched to." (interactive #f(compiled-function () #<bytecode 0xcd38cb2badcec79>)) #<bytecode -0xa08c1727b57641a>)(#<buffer main.rs> nil nil)
  ad-Advice-switch-to-buffer(#f(compiled-function (buffer-or-name &optional norecord force-same-window) "Display buffer BUFFER-OR-NAME in the selected window.\n\nWARNING: This is NOT the way to work on another buffer temporarily\nwithin a Lisp program!  Use `set-buffer' instead.  That avoids\nmessing with the `window-buffer' correspondences.\n\nIf the selected window cannot display the specified buffer\nbecause it is a minibuffer window or strongly dedicated to\nanother buffer, call `pop-to-buffer' to select the buffer in\nanother window.  In interactive use, if the selected window is\nstrongly dedicated to its buffer, the value of the option\n`switch-to-buffer-in-dedicated-window' specifies how to proceed.\n\nIf called interactively, read the buffer name using `read-buffer'.\nThe variable `confirm-nonexistent-file-or-buffer' determines\nwhether to request confirmation before creating a new buffer.\nSee `read-buffer' for features related to input and completion\nof buffer names.\n\nBUFFER-OR-NAME may be a buffer, a string (a buffer name), or nil.\nIf BUFFER-OR-NAME is a string that does not identify an existing\nbuffer, create a buffer with that name.  If BUFFER-OR-NAME is\nnil, switch to the buffer returned by `other-buffer'.\n\nIf optional argument NORECORD is non-nil, do not put the buffer\nat the front of the buffer list, and do not make the window\ndisplaying it the most recently selected one.\n\nIf optional argument FORCE-SAME-WINDOW is non-nil, the buffer\nmust be displayed in the selected window when called\nnon-interactively; if that is impossible, signal an error rather\nthan calling `pop-to-buffer'.  It has no effect when the option\n`switch-to-buffer-obey-display-actions' is non-nil.\n\nThe option `switch-to-buffer-preserve-window-point' can be used\nto make the buffer appear at its last position in the selected\nwindow.\n\nIf the option `switch-to-buffer-obey-display-actions' is non-nil,\nrun the function `pop-to-buffer-same-window' instead.\nThis may display the buffer in another window as specified by\n`display-buffer-overriding-action', `display-buffer-alist' and\nother display related variables.  If this results in displaying\nthe buffer in the selected window, window start and point are adjusted\nas prescribed by the option `switch-to-buffer-preserve-window-point'.\nOtherwise, these are left alone.\n\nReturn the buffer switched to." (interactive #f(compiled-function () #<bytecode 0xcd38cb2badcec79>)) #<bytecode -0xa08c1727b57641a>) #<buffer main.rs>)
  apply(ad-Advice-switch-to-buffer #f(compiled-function (buffer-or-name &optional norecord force-same-window) "Display buffer BUFFER-OR-NAME in the selected window.\n\nWARNING: This is NOT the way to work on another buffer temporarily\nwithin a Lisp program!  Use `set-buffer' instead.  That avoids\nmessing with the `window-buffer' correspondences.\n\nIf the selected window cannot display the specified buffer\nbecause it is a minibuffer window or strongly dedicated to\nanother buffer, call `pop-to-buffer' to select the buffer in\nanother window.  In interactive use, if the selected window is\nstrongly dedicated to its buffer, the value of the option\n`switch-to-buffer-in-dedicated-window' specifies how to proceed.\n\nIf called interactively, read the buffer name using `read-buffer'.\nThe variable `confirm-nonexistent-file-or-buffer' determines\nwhether to request confirmation before creating a new buffer.\nSee `read-buffer' for features related to input and completion\nof buffer names.\n\nBUFFER-OR-NAME may be a buffer, a string (a buffer name), or nil.\nIf BUFFER-OR-NAME is a string that does not identify an existing\nbuffer, create a buffer with that name.  If BUFFER-OR-NAME is\nnil, switch to the buffer returned by `other-buffer'.\n\nIf optional argument NORECORD is non-nil, do not put the buffer\nat the front of the buffer list, and do not make the window\ndisplaying it the most recently selected one.\n\nIf optional argument FORCE-SAME-WINDOW is non-nil, the buffer\nmust be displayed in the selected window when called\nnon-interactively; if that is impossible, signal an error rather\nthan calling `pop-to-buffer'.  It has no effect when the option\n`switch-to-buffer-obey-display-actions' is non-nil.\n\nThe option `switch-to-buffer-preserve-window-point' can be used\nto make the buffer appear at its last position in the selected\nwindow.\n\nIf the option `switch-to-buffer-obey-display-actions' is non-nil,\nrun the function `pop-to-buffer-same-window' instead.\nThis may display the buffer in another window as specified by\n`display-buffer-overriding-action', `display-buffer-alist' and\nother display related variables.  If this results in displaying\nthe buffer in the selected window, window start and point are adjusted\nas prescribed by the option `switch-to-buffer-preserve-window-point'.\nOtherwise, these are left alone.\n\nReturn the buffer switched to." (interactive #f(compiled-function () #<bytecode 0xcd38cb2badcec79>)) #<bytecode -0xa08c1727b57641a>) #<buffer main.rs>)
  switch-to-buffer(#<buffer main.rs>)
  persp-reactivate-buffers((#<buffer main.rs> #<buffer *scratch* (hello)>))
  persp-activate(#s(perspective :name "hello" :buffers (#<buffer main.rs> #<buffer *scratch* (hello)>) :killed nil :local-variables nil :last-switch-time (26141 15510 238197 0) :created-time (26141 15510 217570 0) :window-configuration #<window-configuration> :point-marker #<marker at 48 in main.rs>))
  persp-switch(nil)
  funcall-interactively(persp-switch nil)
  call-interactively(persp-switch nil nil)
  command-execute(persp-switch)

Side note

My lsp servers gets killed for other projects too. Projects such as typescript, python, ruby, go or even c++ project and it is annoying to keep restarting these.

What should happen

Unless I configure this to be so, my lsp servers should not be killed whenever I switch between projects.

My config

**** init-perspective.el
#+begin_src emacs-lisp :tangle ~/.emacs.d/lisp/init-perspective.el :mkdirp yes
  ;;; init-perspective.el --- Provides omnisharp -*- lexical-binding: t -*-
  ;;; Commentary:
  ;; perspective intellisense like bindings
  ;;; Code:

  (unless (package-installed-p 'use-package)
    (package-install 'use-package))

  (use-package perspective
    :diminish
    :commands (persp-switch)
    :config
    (setq persp-state-default-file "~/.emacs.d/persp-confs/persp-auto-save")
    :custom
    (persp-mode-prefix-key (kbd "C-c M-p"))
    :init
    (persp-mode)
    :bind (("s-s" . persp-switch)
           ("s-b" . persp-counsel-switch-buffer)
           ("s-M-b" . persp-ivy-switch-buffer)
           ("C-x b" . persp-switch-to-buffer*)
           ("C-x k" . persp-kill-buffer*)))

  (add-hook 'kill-emacs-hook #'persp-state-save)

  (use-package persp-projectile
    :bind (("s-p" . projectile-persp-switch-project)))

  (provide 'init-perspective)
  ;;; init-perspective.el ends here

#+end_src

**** init-lsp.el
#+begin_src emacs-lisp :tangle ~/.emacs.d/lisp/init-lsp.el :mkdirp yes
  ;;; init-lsp.el --- Provides completion engine -*- lexical-binding: t -*-
  ;;; Commentary:
  ;; Intellisense like binding with lsp-mode
  ;;; Code:

  (maybe-require-package 'use-package)

  ;; Lsp mode seems to require a large amount of memory to work properly
  (setq read-process-output-max (* 10 1024 1024))

  (defun efs/lsp-mode-setup ()
    "Show path to project."
    (setq lsp-headerline-breadcrumb-segments '(path-up-to-project file symbols))
    (add-hook 'before-save-hook #'lsp-organize-imports t t)
    (lsp-headerline-breadcrumb-mode))

  (use-package terraform-mode
    :config
    (terraform-format-on-save-mode t))

  (use-package lsp-mode
    :commands (lsp lsp-deferred)
    :hook  ((terraform-mode . lsp-deff) (lsp-mode . efs/lsp-mode-setup) (web-mode . lsp) (haskell-mode . lsp))
    ;; enable / disable the hints as you prefer:
    :custom
    (lsp-eldoc-render-all t)
    (lsp-idle-delay 0.1)
    (lsp-rust-analyzer-display-lifetime-elision-hints-enable "skip_trivial")
    (lsp-rust-analyzer-display-chaining-hints t)
    (lsp-rust-analyzer-display-lifetime-elision-hints-use-parameter-names nil)
    (lsp-rust-analyzer-display-closure-return-type-hints t)
    (lsp-rust-analyzer-cargo-watch-command "clippy")
    :after
    (which-key)
    :config
    (setq lsp-idle-delay 0.5
          lsp-use-plists t
          lsp-log-io t
          lsp-inlay-hint-enable t
          lsp-eldoc-render-all t
          lsp-clients-typescript-log-verbosity "verbose"
          lsp-headerline-breadcrumb-enable t
          lsp-enable-indentation t
          lsp-restart 'auto-restart
          )
    (lsp-enable-which-key-integration t)
    (setq lsp-keymap-prefix "s-8")
    (add-hook 'lsp-mode-hook 'lsp-ui-mode))

  (use-package lsp-ui
    :diminish
    :hook (lsp-mode . lsp-ui-mode)
    :custom
    (lsp-ui-doc-position 'bottom)
    (lsp-ui-peek-always-show t)
    (lsp-ui-sideline-show-hover t)
    (lsp-ui-doc-enable t)
    :config
    (setq lsp-ui-doc-enable t
          lsp-ui-peek-enable t
          lsp-ui-sideline-enable t
          lsp-ui-imenu-enable t
          lsp-ui-flycheck-enable t))

  (use-package lsp-treemacs
    :diminish
    :after (lsp treemacs)
    :commands (lsp-treemacs-errors-list)
    :init (lsp-treemacs-sync-mode 1))

  (use-package lsp-ivy
    :diminish
    :after (lsp ivy))

  (dolist (mode '(c-mode-hook
                  css-mode-hook
                  dockerfile-mode-hook
                  js-mode-hook
                  less-css-mode-hook
                  php-mode-hook
                  python-mode-hook
                  rust-mode-hook
                  sass-mode-hook
                  scss-mode-hook
                  terraform-mode-hook
                  typescript-mode-hook
                  yaml-mode-hook
                  c++-mode-hook))
    (add-hook mode 'lsp-deferred))

  (use-package dap-mode
    ;; Uncomment the config below if you want all UI panes to be hidden by default!
    ;; :custom
    ;; (lsp-enable-dap-auto-configure nil)
    ;; :config
    ;; (dap-ui-mode 1)
    :after (general)
    :config
    ;; (require 'dap-node)
    ;; (dap-node-setup) ;; Automatically installs Node debug adapter if needed
    (dap-ui-mode 1)
    (dap-ui-controls-mode 1)
    ;; Set up Node debugging
    (require 'dap-node)
    (require 'dap-chrome)
    (require 'dap-edge)
    (require 'dap-firefox)
    ;; installs node, chrome, edge and firefox extension in .extension/vscode
    (dap-node-setup)
    (dap-edge-setup)
    (dap-chrome-setup)
    (dap-firefox-setup)

    ;; Set up Go debugging
    (require 'dap-go)
    (require 'dap-dlv-go)
    ;; installs go extension in .extension/vscode
    (dap-go-setup)

    ;; Set up debugging for php
    (require 'dap-php)
    ;; installs php extension in .extension/vscode
    (dap-php-setup)

    ;; Set up debugging CPPtools
    (require 'dap-cpptools)
    ;; installs cpp extension in .extension/vscode
    (dap-cpptools-setup)

    ;; LLDB used for rust and other environment debugging
    (require 'dap-lldb)
    (require 'dap-gdb-lldb)
    ;; installs lldb extension in .extension/vscode
    (dap-gdb-lldb-setup)

    (when (executable-find "lldb-mi")
      (dap-register-debug-template
       "Rust::LLDB Run Configuration"
       (list :type "lldb-mi"
             :request "launch"
             :name "LLDB::Run"
             :gdbpath "rust-lldb"
             :target nil
             :cwd nil)))

    (setq dap-python-debugger 'debugpy)
    ;; Bind `C-c l d` to `dap-hydra` for easy access
    (general-define-key
     :keymaps 'lsp-mode-map
     :prefix lsp-keymap-prefix
     "d" '(dap-hydra t :wk "debugger")))

  (setq lsp-keymap-prefix "s-8")
  ;; Set up lsp-eslint
  ;; (setq lsp-eslint-server-command
  ;; '("node"
  ;; "/Users/randyburrell/.emacs.d/.extension/vscode/dbaeumer.vscode-eslint-2.1.19/extension/server/out/eslintServer.js"
  ;; "--stdio"))

  (provide 'init-lsp)
  ;;; init-lsp.el ends here

#+end_src

Help needed

I think a big problem is lsp-mode trying to run while there is no lsp rust analyzer server running. I would like to keep my servers running even while switching between perspective projects. Is there a way to do this? Or can someone point me in the right direction?

commented

There might be some incompatibilities between lsp-mode and Perspective. I don't use lsp-mode myself, so this'll take some time for me to get to set up and debug. From the stack trace (thank you for including it, btw!) it looks like lsp-mode doesn't like the way Perspective uses switch-to-buffer advice, which is a pretty fundamental implementation detail.

A couple of questions:

  1. What version of Emacs are you using?
  2. Any chance you could try Eglot in the meanwhile? I've used it without problems.

Hey @gcv, thanks for the quick response. I'm currently using version 29.3 from https://emacsformacosx.com.

I don't have any experience using Eglot but I would use it with rust in the meanwhile if it provided better or near as good features as lsp-mode. I know lsp is kinda inefficient and Emacs comes with support for Eglot builtin but I haven't found the time to migrate yet. Plus lsp integrates well with dap-mode but that is not really an issue for me.

Hey @gcv I have switched completely over to Eglot. However, I'm now getting this error when I try to switch burgers:

Debugger entered--Lisp error: (wrong-type-argument hash-table-p "Unprintable entity")
  maphash(#f(compiled-function (_ v) #<bytecode 0x5b460e5790cc9fb>) "Unprintable entity")
  persp-names()
  persp-current-buffers*(t)
  persp-current-buffer-names(t)
  byte-code("\10\204\10\0\11\204\21\0\303\32\304\305!)\2027\0\306\307!\310\311p!!\312\313\314\3\203'\0\313\315\5\"\202(\0\316\"\317\320\5\"\303\211\211\211\6..." [current-prefix-arg persp-mode read-buffer-function nil read-buffer-to-switch "Switch to buffer" persp-current-buffer-names t buffer-name persp-other-buffer completing-read format "Switch to buffer%s: " " (default %s)" "" make-closure #f(compiled-function (string predicate action) #<bytecode 0xb6ce81976bc39b3>)] 10)
  call-interactively(persp-switch-to-buffer* nil nil)
  command-execute(persp-switch-to-buffer*)

Any ideas?

commented

I'm not sure. The byte-code error makes me suspicious that your installation's .elc files have become corrupted somehow. What if you uninstall Perspective (M-x package-delete) and reinstall it again?

Are you using unusual Unicode or emoji characters in your perspective names? That should work, but maybe it doesn't for some reason.

Does this happen even if you don't have any buffers open that want to use LSP/Eglot?

I'm not using unicode characters in any of the perspective names and reinstalling leads to the same outcome. This have been happening without any Eglot servers running.

One thing I noticed is that if I disable then re-enable perspective then it works fine but then it has already messed up most of my buffers by that time.

I can't understand why this is happening no matter how long I work on this. I have been using perspectives for years and it frustrates me that this is causing such a problem. BTW, thank you for the work that you have put into this package.

commented

The byte-code errors in the stack trace you posted make me suspicious about the state of your Emacs environment. I have run into this before when I ran new major versions of Emacs but used byte-code generated by older versions, and have since moved to a setup where package-user-dir always contains emacs-major-version. IOW, all my packages are always installed into a directory that contains the Emacs major version to avoid potentially cross-contaminating the installation with incompatible byte-code.

You can try to debug if that's happening to you in one of two ways:

  1. If you have a stable use-package or similar method to reinstall all your packages and recreate your setup, I suggest you quit Emacs, delete your package-user-dir, and let your setup reinstall everything. Then restart Emacs and see if the problem persists.

  2. You can also try just deleting all .elc files under package-user-dir and then restart Emacs. This will force Emacs to just use non-byte-compiled Elisp source, and will slow everything down, but will rule out any issues with the byte-compiler. If this solves the problem, you can either reinstall everything as above, or force Emacs to recompile everything. (You can do this by traversing package-user-dir and calling byte-recompile-directory on all entries.)

Let me know how it goes and if you need help with any of those steps. The problem you're seeing really doesn't seem like a bug in Perspective at the moment, though maybe something Perspective does triggers it somehow.

@gcv I finally figured out what was happening.

It turns out that desktop-save-mode was on all this time and that was what caused this error. Unfortunately, I took this long to figure out that a package I was using had this turned on.

Thank you very much for your help.