lsp-mode feature wishlist

Created on 7 Dec 2018  路  93Comments  路  Source: emacs-lsp/lsp-mode

discussion

Most helpful comment

Not sure if this falls within lsp, or lsp-ui, or company, but:

Would be great to see parameter hints (and/or ability to auto-fill them) when completing with lsp.

For example, VsCode would do the following:

All 93 comments

I believe that lsp-mode needs to enhance it's lsp-rename function because that will get Emacs closer to being a full featured IDE. For example we could get some ideas from what intellij provides.Also i think the flymake integration will be a huge + but i think that is closer to be complete as @yyoncho has started working on it.

@innerout can you elaborate on rename improvements?

An example from intellij
image
image
images taken from here https://www.jetbrains.com/help/idea/refactoring-source-code.html .
Also another interesting feature would be this https://www.jetbrains.com/help/idea/find-and-replace-code-duplicates.html .

@innerout we are limited by the lsp protocol and the language servers so features that are not supported by the server(s) are out of scope.

Hey, thank you for the great work!

I think it's good to have a set of customization option and a better default, because some users may not need so much powerful functionality. For instance, most of them just want a fast way to completion, find references and maybe check doc. It's sure that we may turn on some options as default for user to test and whom really want to use.

It may too early to say that easy to configure is also an important feature, but it should be taken into consideration : )

Would have to be thought about a bit, but in light of the new session system, would be nice to be able to persist a session across emacs reboots.

I work on fairly large, enterprisey projects that take forever to get fully processed by my language server (php). Sometimes I end up restarting emacs for whatever reason, and then have to start the process all over again.

Getting started / specific language server installation instructions could be made more obvious, instead of making user go to specific language server docs, understand them properly, and then translate back to getting it to work with emacs.

For me, I generally like to just copy and paste instructions..

Not sure if this falls within lsp, or lsp-ui, or company, but:

Would be great to see parameter hints (and/or ability to auto-fill them) when completing with lsp.

For example, VsCode would do the following:

@kaiwk thanks for the suggestion. For now I will add quick start section similar to the one in https://github.com/emacs-lsp/lsp-java which you could just copy/paste and then start coding. We may investigate creating a meta package which joins all the packages that are needed for normal development similar to spacemacs layers. Also, if you have concrete proposals, please go ahead and file an issue or provide a PR.

@tam5

Would have to be thought about a bit, but in light of the new session system, would be nice to be able to persist a session across emacs reboots.

I work on fairly large, enterprisey projects that take forever to get fully processed by my language server (php). Sometimes I end up restarting emacs for whatever reason, and then have to start the process all over again.

can you elaborate a bit more? Unless there is a but the session should be persisted in lsp-session-file and then you won't be asked for root selection any more in any of the already imported projects, or you mean something else?

@tam5

Getting started / specific language server installation instructions could be made more obvious, instead of making user go to specific language server docs, understand them properly, and then translate back to getting it to work with emacs.

For me, I generally like to just copy and paste instruct

I believe we can do better: https://github.com/emacs-lsp/lsp-mode/issues/506 . I will try to let lsp-mode directly consume the VSCode packages so you can just open the file and it will automatically download server, autoconfigure itself and so on.

About the signature feature: we have some code in, but I have never had a time to test it whether it works fine but in general this feature should work out of the box(currently it doesn't). Here it is a bug tracking the issue: https://github.com/emacs-lsp/lsp-mode/issues/214

@yyoncho Awesome, thanks for your responses, and thanks for all your hard work!

I'll respond to the features that already seem to have issues in their rightful place, sorry for not searching first.

Regarding the session persistence:

The session is indeed persisted, to a degree, in the lsp-session-file which gives two very nice features regarding persistence:
1) No more asking for project root
2) Deleting all buffers within a project, does not shut down my language server, which is great

What I would like to potentially add:
Depending on some customization of course, when exiting emacs, do not shut down my language server. This way, I can come right back to emacs and continue my work without waiting for my language server to boot up and do all its file parsing.

This would mean preventing shut down of language server on exit, as well as reconnecting to the existing server on startup.

The only issue I see is potentially having dangling language server processes if they are not cleaned up properly.

Depending on some customization of course, when exiting emacs, do not shut down my language server. This way, I can come right back to emacs and continue my work without waiting for my language server to boot up and do all its file parsing.

I believe that this is not feasible due to multiple reasons - in general re-connection is not possible in LSP protocol, Re-connection to STDOUT is not technically possible(or at least I don't know how to do that). The only way to implement this feature is either to have some kind of server support for that or implement a proxy in front of the language server which is separate project itself.

You can read stdout and write to stdin of any process if you know its pid.

For stdin

cat main.c > /proc/<pid>/fd/0

For stdout

tail -f /proc/<pid>/fd/1

Here's another one:

Would it be possible to generate doc-blocks (i.e. be able to read a method signature and insert proper doc block including relevant things like param names/types, return type, etc. ?

There are different styles and conventions for this, so I'm not sure how the customization of that would be handled.

Making a mode for show-documentation-at-point and maybe other future "transient" or "help" buffers. That way we can create key-maps for the help-buffers.

@tam5
for doc-blocks we should have support from the server. For example, JDT LS supports them although it is not exposed in lsp-java. If you want that functionality for particular language go and file a bug on the server side and then we can expose in lsp-mode.

@blasut
Make sense - in general, doc buffers functionality is not polished(e. g. links does not work all the time). Currently, we are using view-mode and I have bound q to exit it.

According to specification rootPath and rootUri can be null on initialization request if no folder is open.

It allows to open the file in the home folder and not make lsp server to treat home folder as a project.

Is it possible with lsp-mode?

@muffinmad currently we do not support that. IIRC some of the servers crash if there is no associated project(e. g. rls) so before jumping into implementing this we should investigate whether the servers are following the spec.

Can you share your usecase?

I want to be able to open files outside project directory.
Opening ~/test.py and passing home folder as rootPath makes Microsoft pyls analyze my whole home folder which is little annoying.

@muffinmad Did you try (setq lsp-auto-guess-root nil)?

@muffinmad I tested mspyls and it works fine if you do not pass rootUri/root in initialization options. We have to figure out what should be the flow to enable this feature. In vscode the folder selection is not interactive and user has to add the folders upfront in order to start the language server for that folder. We could have a setting which enables that same behaviour in lsp-mode or we could have a different flow like -

"Add one more option to lsp menu which will be - Connect/start rootless language server."

This flow will work, but the result will be that you will be asked that question each time you open a file and you do not want to add it as a root(which btw could be avoided by adding a dir local variable which will mark the files in the directory as rootless).

WDYT?

@seagle0128

@muffinmad Did you try (setq lsp-auto-guess-root nil)?

This will start a language server in the home dir or it won't start a server at all. I think that OP is looking for different solution.

@seagle0128 After setting lsp-auto-guess-root to nil lsp gives me options to:

  • do nothing
  • blackist folder and do nothing
  • run server but pass some folder as rootPath

So it's lsp-mode in first place who won't let me go without specifying the project :)

@yyoncho How about not to require project but give option to specify it if user not satisfied with one guessed by lsp-mode?

I think the question that lsp asks first is little discouraging.
I'm opening file in VC controlled project and Emacs asks me "foo.py is not part of any project. Select action: ". After hitting TAB I see that project is guessed correctly in the first completion option: "add ~/workspace/project/ to blacklist".

What about this:

  1. Make lsp-mode-default-get-root-function function which will:
  2. Look for some variable like lsp-mode-project-root. It can be set in .dir-locals.el
  3. Try determine project by project-current, some-projectile-function, etc.
  4. Here can be option like lsp-mode-ask-if-no-project. If it is set then ask question about project root else nil or default-directory

  5. Make customizable variable like lsp-mode-get-root-function so user can (cdr (project-current)) instead of default one

This way new lsp-mode users will recieve working lsp server instantly.
And there will be options to configure some exotic project roots :)

@yyoncho How about not to require project but give option to specify it if user not satisfied with one guessed by lsp-mode?

There is such an option in the menu - "Import project by selecting root directory interactively.".

I think the question that lsp asks first is little discouraging.
I'm opening file in VC controlled project and Emacs asks me "foo.py is not part of any project. Select action: ". After hitting TAB I see that project is guessed correctly in the first completion option: "add ~/workspace/project/ to blacklist".

For better UE I suggest you use helm-mode.

I have to think more about your other suggestions. Generally, we support plugging guess root function via projectile or project.el (e. g. you could use projectile-project-root-files-functions).

  • Here can be option like lsp-mode-ask-if-no-project. If it is set then ask question about project root else nil or default-directory

What you are proposing is to use this variable as dir local for example and configure the home directory as a directory containing single file workspaces? This mechanism will work in general but in your particular case will force lsp-mode to use no-root workspaces for everything under ~/. Also, this flag sounds like an addition to the interactive options I suggested here https://github.com/emacs-lsp/lsp-mode/issues/515#issuecomment-509736087. I prefer the default behaviour of lsp-mode to be interactive so the users could figure out what is happening and in addition to there is a set of settings which makes lsp-mode less verbose for the advanced users.

I prefer the default behaviour of lsp-mode to be interactive so the users could figure out what is happening and in addition to there is a set of settings which makes lsp-mode less verbose for the advanced users.

And I, in turn, prefer do-what-i-mean behaviour as much as posible. I.e. run lsp server in current project without no questions. But lets leave defaults for now.

I achieved desired behaviour by setting lsp-auto-guess-root to t. Works perfect. But opening file in the folder for which lsp-mode can't guess project root leads to "LSP :: test.py not in project or it is blacklisted." error.
I need the option like lsp-allow-no-project. Setting this variable will cause lsp to run lsp server with nil passed as rootUri and don't store any folder in lsp-sessions.

@muffinmad

I achieved desired behaviour by setting lsp-auto-guess-root to t. Works perfect. But opening file in the folder for which lsp-mode can't guess project root leads to "LSP :: test.py not in project or it is blacklisted." error.
I need the option like lsp-allow-no-project. Setting this variable will cause lsp to run lsp server with nil passed as rootUri and don't store any folder in lsp-sessions.

This makes sense as the non-interactive/auto-guess option which will be an addition to the interactive described above. I think that we are ready to create an issue.

I want to echo @muffinmad's comments. I think the default behavior of prompting for project root is lsp-mode's biggest usability issue. Personally I don't want to set lsp-auto-guess-root because we use a monorepo and I don't want the git root as my root dir for all languages. What I would like is language specific logic. For example go-mode files look for a "go.mod" file to find module root, or check if file is under GOPATH. For java, it looks for the "pom.xml" or whatever other project markers there are. If it can't find language specific markers then it falls back to generic project detection (i.e. VCS folder). If there is still no project, it would default to the file's directory, or perhaps no directory and just the single file.

Related, I would also like an easy way to change the current root folder. For example, if lsp-mode guesses the wrong folder, you can run lsp-workspace-update-folder or whatever and choose a different folder (and then lsp-mode automatically restarts the workspace with the new folder).

This makes sense as the non-interactive/auto-guess option which will be an addition to the interactive described above. I think that we are ready to create an issue.

Created https://github.com/emacs-lsp/lsp-mode/issues/944.

Personally I don't want to set lsp-auto-guess-root because we use a monorepo and I don't want the git root as my root dir for all languages. What I would like is language specific logic. For example go-mode files look for a "go.mod" file to find module root, or check if file is under GOPATH. For java, it looks for the "pom.xml" or whatever other project markers there are. If it can't find language specific markers then it falls back to generic project detection (i.e. VCS folder). If there is still no project, it would default to the file's directory, or perhaps no directory and just the single file.

This is supported right now and a lot of users use lsp-mode like that. To achieve this state do:

  1. set lsp-auto-guess-root to t
  2. customize the projectile root selection functions with your custom language-specific functions.

Project root selection is a complex thing and thus we delegate it to the projectile/project.el.

Related, I would also like an easy way to change the current root folder. For example, if lsp-mode guesses the wrong folder, you can run lsp-workspace-update-folder or whatever and choose a different folder (and then lsp-mode automatically restarts the workspace with the new folder).

Something like that should work:

(defun my/lsp-workspace-folder-update ()
  (interactive)
  (lsp-workspace-folders-remove (lsp-workspace-root))
  (lsp-workspace-folders-add (read-directory-name "Select workspace folder to add: "
                                                  default-directory
                                                  nil
                                                  t))
  (lsp))

Would it be possible to have some kind of iterative compilation process, a la xCode or Visual Studio? Here's the behavior I'm really looking for:

1) I edit a header file in a C++ project which is included in files a.cpp and b.cpp (either or neither of which may currently be opened in emacs)
2) Some compilation command runs (either automatically if preferred, or manually via a keybind)
3) The treemacs-errors-list window is automatically updated with anything broken due to the change in the header file

This would help tremendously when refactoring. If performance is a concern, the compilation process could by default be triggered manually, but some kind of integration with compilation-mode/compilation buffer would be awesome.

Edit - Really not sure if this is in the domain of lsp-mode or ccls/clangd, but I thought I'd throw it out there.

@aschneid1 at client side we have support for that as long as there is a way to force republishing the diagnostics from the server(e. g. trigger the recompilation). In some of the servers, this is happening automatically. I am cc-ing @MaskRay in case he wants to elaborate on how to force server to re-compile(TBO I was expecting this to happen automatically, so this might be a bug).

@muirrn

What I would like is language specific logic. For example go-mode files look for a "go.mod" file to find module root, or check if file is under GOPATH.

(require 'f)
(defun my-try-go-mod (dir)
  "Find go project root for DIR."
  (if (and dir
           (not (f-descendant-of-p dir (or (getenv "GOPATH")
                                           (concat (getenv "HOME") "/go")))))
      (let
          ((result nil)
           (cur-dir dir))
        (while (or (not result) (f-equal-p cur-dir "/"))
          (if (f-exists-p (concat cur-dir "/go.mod"))
              (setq result cur-dir)
            (setq cur-dir (f-dirname cur-dir))))
        (if result
            (cons 'vc result)
          (cons 'vc dir)))
    (if dir
        (cons 'vc dir)
      nil)))


(defun my-go-project-setup ()
  "Set project root for go project."
  (setq-local project-find-functions (list #'my-try-go-mod #'project-try-vc))
  (setq-local lsp-auto-guess-root t))

(add-hook 'go-mode-hook #'my-go-project-setup)

@s-kostyaev Why 'vc as project type in result? Maybe 'transient instead?

Take a look at locate-dominating-file function: (locate-dominating-file dir "go.mod")

Why 'vc as project type in result? Maybe 'transient instead?

Because when I was trying to fix same problem I read how 'vc projects work in project.el. Maybe this can be improved but it works for me. I'll see transient projects when I will have enough time.

Take a look at locate-dominating-file function: (locate-dominating-file dir "go.mod")

Thanks. I'll fix it.

locate-dominating-file

You may take a look at projectile-root-top-down/projectile-root-bottom-up as well.

@yyoncho I don't use projectile

Improved version:

(defun my-try-go-mod (dir)
  "Find go project root for DIR."
  (if (and dir
           (not (f-descendant-of-p dir (or (getenv "GOPATH")
                                           (concat (getenv "HOME") "/go")))))
      (let ((result (locate-dominating-file dir "go.mod")))
        (if result
            (cons 'transient (expand-file-name result))
          (cons 'transient dir)))
    (when dir
        (cons 'transient dir))))

thanks @muffinmad

@s-kostyaev One little improvement: use when instead of if that returns nil if condition is not met.

On "give all diagnostics when a common header is edited":

@aschneid1 I think this mostly fall in the realm of customization. LSP doesn't provide any mechanism for dependency information among files, so you have to customize it differently for each language server. Basically,

  1. When a .cc file is opened, retrieve dependency information with ccls-file-info (or other language server specific extension) and record it.
  2. When a .h is edited (e.g. adding a hook to after-change-functions), look it up which .cc files directly/transitively include it and call (lsp-on-change 0 0 0) on their buffers. You definitely need some debounce mechanism (see the implementation of lsp-on-change for some insight), and some protective checks (limiting the number of files, etc), because this is likely a serious performance issue if not used wisely.

(https://github.com/MaskRay/ccls/issues/439#issuecomment-513453182)

I finally had a chance to play around with this a bit. I've managed to get the basic skeleton of this working, but I'm running into an issue with (lsp-on-change). Here's the chunk of code I've cobbled together so far:

(setq ccls-file-dependency-hash (make-hash-table :test 'equal))

;;Setup initial blacklist locations
(setq ccls-path-prefix-blacklist '("/usr/local/include"  "/usr/local" "/usr/include"
                                   "/Applications/Xcode.app" "/Library/Developer"))

(require 'seq)

;; check if a string matches a pattern
(defun file-blacklisted-p (file)
  "Checks if the passed file path is part of ccls-path-prefix-blacklist"
  (not (string-match-p (concat (regexp-opt ccls-path-prefix-blacklist)  ".*")
                  file)))

;; remove items that are blacklisted
(defun insert-into-hash ()
  "Inserts the dependencies of the currently selected buffer into ccls-file-dependency-hash"

  ;;Fails if ran by adding hook to c++-mode directly.  I think it's
  ;;because the LSP client hasn't been started/initialized, yet?  If
  ;;there's a ccls hook to notify that the client for a file is up and
  ;;running that would be better
  (when (eq major-mode 'c++-mode)
    (let ((file-list (gethash "dependencies"
                              (ccls-file-info '(:includes t :dependencies t)))))
      (dolist (file-dependency (seq-filter 'file-blacklisted-p file-list))
        (let ((dependency-list (gethash file-dependency ccls-file-dependency-hash)))
          (setf (gethash file-dependency ccls-file-dependency-hash)
                (add-to-list 'dependency-list buffer-file-name)))))))


(add-hook 'before-save-hook 'insert-into-hash)

;; make myBuf current temporarily
(defun notify-buffer-changed (file &optional force-open)
  ;;; Check if a buffer corresponding to file is open.  If so, send notification to lsp-server
  (if force-open
      (find-file-noselect file))
  (if (find-buffer-visiting file)
        (with-current-buffer (find-buffer-visiting file)
          (message "Checking file %s in buffer %s" file (current-buffer))
          (lsp-on-change 0 0 0))))



(defun notify-dependencies-of-change (&optional force-open depth file)
  (unless file (setq file buffer-file-name))
  (unless depth (setq depth 2))
  (message "file: %s and dpeth: %s" file depth)
  (if (> depth 0)
        (let ((file-list (gethash file ccls-file-dependency-hash)))
          (dolist (file-dependency file-list)
            (notify-buffer-changed file-dependency force-open)
            (notify-dependencies-of-change force-open (- depth 1) file-dependency)))))

After opening and saving a couple of files, some entries in the hash table should be populated. After changing a header file (in one of the registered dependencies), you can call notify-dependencies-of-change to traverse the files that depend on the altered header file (up to a default depth of 2). However, I'm currently running into a wall with the call to lsp-on-change in notify-buffer-changed. I currently get the error

Debugger entered--Lisp error: (args-out-of-range # 0 0)

I've tried playing with the parameters a bit (for instance, changing 0 0 0 to 1 (buffer-size) 0), but that just induces a lot of incorrect error reports from ccls. I've looked at the implementation of lsp-on-change but I'm struggling to find any actual uses of the function that I could use as an example. Any advice for me?

Also, apologies for the rough draft of the elisp above. Still very much a work in progress.

I've tried playing with the parameters a bit (for instance, changing 0 0 0 to 1 (buffer-size) 0), but that just induces a lot of incorrect error reports from ccls. I've looked at the implementation of lsp-on-change but I'm struggling to find any actual uses of the function that I could use as an example. Any advice for me?

(let ((lsp-document-sync-method 'full))
      (lsp-on-change (point-min) (point-max) (- (point-max)
                                                (point-min))))

Maybe something like that would do.

I've tried playing with the parameters a bit (for instance, changing 0 0 0 to 1 (buffer-size) 0), but that just induces a lot of incorrect error reports from ccls. I've looked at the implementation of lsp-on-change but I'm struggling to find any actual uses of the function that I could use as an example. Any advice for me?

(let ((lsp-document-sync-method 'full))
      (lsp-on-change (point-min) (point-max) (- (point-max)
                                                (point-min))))

Maybe something like that would do.

Still no luck. The buffer doesn't register errors inline, and neither treemacs-error or the flycheck error list discover the compilation error until after I re-save the file (by typing a space, deleting it, and hitting save). The three arguments to lsp-on-change appear to be correct, though.

Interestingly (maybe?) if I directly execute the (lsp-on-change (point-min) (point-max) (- (point-max) (point-min)))) in the buffer with the expected error, it also doesn't work (i.e., the buffer is active, I hit M-:, call lsp-on-change, and nothing is updated and the function returns nil).

It might be caused by ccls being smart and detecting that there are no changes. I think that if you call lsp--text-document-did-open this will force rechecking the buffer, can you try that?

It might be caused by ccls being smart and detecting that there are no changes. I think that if you call lsp--text-document-did-open this will force rechecking the buffer, can you try that?

did-open failed with an error about workspace being nil or something of the sort. However, I noticed that touching the buffer (without modifying or saving it) didn't work, but saving a buffer with no modifications did. So:

;; make myBuf current temporarily
(defun notify-buffer-changed (file &optional force-open)
  ;;; Check if a buffer corresponding to file is open.  If so, send notification to lsp-server
  (if force-open
      (find-file-noselect file))
  (if (find-buffer-visiting file)
      (progn
        (with-current-buffer (find-buffer-visiting file)
          (message "Checking file %s in buffer %s" file (current-buffer))
          (let ((lsp-document-sync-method 'full))
            (lsp--text-document-did-save))))))

...works. I'm not sure if lsp--text-document-did-save has any side effects I need to be aware of, but everything seems to be working like I want it to. I'm gonna tinker a bit more with it, but I'll post a final version of the functions in a few days if anyone else would like to use them.

I'm gonna tinker a bit more with it, but I'll post a final version of the functions in a few days if anyone else would like to use them.

IMO it will be useful to have it in a wiki page.

I'm gonna tinker a bit more with it, but I'll post a final version of the functions in a few days if anyone else would like to use them.

IMO it will be useful to have it in a wiki page.

Sure. Which wiki? It'd be my first contribution. Here's something of a final version.

  (setq ccls-hash-file-location "~/.emacs.d/ccls_hash.txt")
  (setq ccls-file-dependency-hash (make-hash-table :test 'equal))

  ;;Setup initial blacklist locations
  (setq ccls-path-prefix-blacklist '("/usr/local/include"  "/usr/local" "/usr/include"
                                     "/Applications/Xcode.app" "/Library/Developer"))

  (require 'seq)
  (defun load-hash-from-file ()
    "Loads file dependency hash from file specified in ccls-hash-file-location"
    (if (file-exists-p ccls-hash-file-location)
        (with-temp-buffer
          (insert-file-contents ccls-hash-file-location)
          (setq ccls-file-dependency-hash (read (buffer-string))))))

  (defun write-hash-to-file (file hash)
    (with-temp-file file (insert (format "%s" (prin1 hash)))
                    nil))

  ;; check if a string matches a pattern
  (defun file-blacklisted-p (file)
    "Checks if the passed file path is part of ccls-path-prefix-blacklist"
    (not (string-match-p (concat (regexp-opt ccls-path-prefix-blacklist)  ".*")
                         file)))

  ;; remove items that are blacklisted
  (defun insert-into-hash ()
    "Inserts the dependencies of the currently selected buffer into ccls-file-dependency-hash"

    ;;Fails if ran by adding hook to c++-mode directly.  I think it's
    ;;because the LSP client hasn't been started/initialized, yet?  If
    ;;there's a ccls hook to notify that the client for a file is up and
    ;;running that would be better
    (when (eq major-mode 'c++-mode)
      (let ((file-list (gethash "dependencies"
                                (ccls-file-info '(:includes t :dependencies t)))))
        (dolist (file-dependency (seq-filter 'file-blacklisted-p file-list))
          (let ((dependency-list (gethash file-dependency ccls-file-dependency-hash)))
            (setf (gethash file-dependency ccls-file-dependency-hash)
                  (add-to-list 'dependency-list buffer-file-name))))
        (write-hash-to-file ccls-hash-file-location ccls-file-dependency-hash))))

  ;; Update the hash on each save (includes writing it out to a file)
  (add-hook 'before-save-hook 'insert-into-hash)

  (defun notify-buffer-changed (file &optional force-open)
    ;; Check if a buffer corresponding to file is open.  If so, send notification to lsp-server
    (if force-open
        (find-file-noselect file))
    (if (find-buffer-visiting file)
        (progn
          (with-current-buffer (find-buffer-visiting file)
            (let ((lsp-document-sync-method 'full))
              (lsp--text-document-did-save))))))

  (defun get-dependencies (depth file full-dependency-list)
    "Recursively builds unique list of dependencies to prevent  redundant calls to notify-buffer-changed"
    (if (> depth 0)
        (progn
          (let ((file-list (gethash file ccls-file-dependency-hash)))
            (dolist (file-dependency file-list)
              (add-to-list 'full-dependency-list file-dependency)
              (get-dependencies (- depth 1) file-dependency full-dependency-list)))))
    full-dependency-list)

  (defun notify-dependencies-of-change (&optional force-open depth file)
    ;; Notify any dependencies of the current buffer that the file has changed
    (unless file (setq file buffer-file-name))
    (unless depth (setq depth 2))
    (dolist (file-dependency (get-dependencies depth file nil))
      (notify-buffer-changed file-dependency force-open)))

  (defun my-notify-dependencies ()
    (interactive)
    ;; Helper function to specify paramters for notify-dependencies-of-change...
    (notify-dependencies-of-change t 4))

file-location and blacklist can be customized. file-location just stores generated dependency hash table on disk so you don't lose the information between emacs close/opens, while blacklist refrains from putting files in those directories in the hash as dependencies. my-notify-dependencies is just a helper function so I can easily call update using a hydra bind. I've added a helper function get-dependencies that gathers all of the dependencies first to prevent redundant calls to notify-buffer-changed (which probably ought to be renamed now that I look at it to something like notify-dependency-changed).

Seems to do the trick alright.

Sure. Which wiki? It'd be my first contribution. Here's something of a final version.

This: https://github.com/emacs-lsp/lsp-mode/wiki or CCLS wiki if @MaskRay is interested in that piece of code because it is kind of ccls specific. I guess it could be ccls.el module.

A function akin to flycheck-verify-setup or company-diag would be really useful, especially when getting started/trying it out.
Thanks!

@nverno tracked by https://github.com/emacs-lsp/lsp-mode/issues/764 . Feel free to comment in #764 if you want to have particular information in the describe session dashboard.

@yyoncho I knew it must be somewhere, but missed it somehow.

A function to toggle symbol highlighting at will (and not just set once the global lsp-enable-symbol-highlighting).

I used to have this:

(defun lsp-toggle-highlighting ()
  (interactive)
  (if lsp-enable-symbol-highlighting
      (progn (setq lsp-enable-symbol-highlighting nil)
             (message "LSP symbol highlighting OFF"))
    (setq lsp-enable-symbol-highlighting t)
    (message "LSP symbol highlighting ON")))

But it looks like it doesn't work anymore.

@lerouxrgd the change was needed in order to make lsp-mode faster (we do not check whether highlights are enabled after each user action)

This will work:

(defun lsp-toggle-highlighting ()
  (interactive)
  (setq lsp-enable-symbol-highlighting (not lsp-enable-symbol-highlighting))
  (cond
   ((and lsp-enable-symbol-highlighting  (lsp--capability "documentHighlightProvider"))
    (add-hook 'lsp-on-idle-hook #'lsp--document-highlight nil t)
    (lsp--info "Highlighting enabled."))
   ((not lsp-enable-symbol-highlighting)
    (remove-hook 'lsp-on-idle-hook #'lsp--document-highlight t)
    (lsp--remove-overlays 'lsp-highlight)
    (lsp--info "Highlighting disabled."))
   (t (user-error "Current server does not support highlights?"))))

Does lsp-mode support the folding range request?

@Vincent-P - yes, check lsp-origami.

Oh ok thanks, I couldn't find anything about folds in the README.

@Vincent-P - it will be added, thank you.

Hi all, I'm wondering about the following :

I'm trying to setup a webdev environment in emacs and I'm trying to copy a feature of VSCode that I find very helpful and I can't seem to do it.

Basically, in VSCode when you start typing a tag like : h1, it'll prompt a completion with <TAB>. It also works for tags attributes.

In emacs (afaik) you have to type much more, at least <h1><\ for completion or use a different snippet for each level/tag.

My probably incomplete understanding was that using lsp with company, either in html-mode or web-mode, I should be able to come to something close or comparable.

Another user on reddit mentions this in response :

[Trace - 7:58:44 AM] Sending request 'html/tag - (138)'. Params: { "textDocument": { "uri": "file:///home/kyoncho/Sources/lsp-docker/demo-projects/Scala/hello/src/test/scala/example/foo.html" }, "position": { "line": 0, "character": 6 } }

[Trace - 7:58:44 AM] Received response 'html/tag - (138)' in 1ms. Result: "$0</html>" 

which is apparently additional VSCode behavior not being implemented in lsp-mode, could this feature be implemented ?

Thanks for the good work

@Nathan-Furnal while i don't really see that as being part of or related to lsp, but i could hear the argument.

Generally though, if you haven't seen https://github.com/smihica/emmet-mode, you should check it out. Based on what you are describing it sounds up your alley and it should have what you are looking for +more. It's not exactly autocomplete, but to me it makes more sense for web stuff

@tam5 I think it makes sense for ease of use because I have and will have to type lots of those tags and I suppose a lot of people are too, which on even a medium sized project, when added together can become quite a drag.

I don't have a more meaningful point and most ppl could probably create snippets for that I suppose.

I'm using html-mode and emmet-mode as you suggested and it fits my needs.

On my end, this issue can be closed unless we want to dig deeper in the feature, thanks for your help!

@tam5

@Nathan-Furnal while i don't really see that as being part of or related to lsp, but i could hear the argument.

In this case, the language server provides that functionality and in lsp-mode we try to squeeze every functionality that the server provides. In general, you are right than experienced html developer will do better with emmet but unless I am mistaken the emmet tags do not provide context-sensitive completion so a beginner might benefit from autocompletion+auto closing the tags(the vscode behavior).

Hi. Does lsp-mode support for opening other window when go to definition like godef-jump-other-window in go-mode. Or is there any way to do that? @yyoncho

@tianhongw like xref-find-definitions-other-window ?

@tianhongw like xref-find-definitions-other-window ?

Thanks a lot! That is exactly what i want.

Make lsp--auto-configure a tad more configurable, or extract sub-functions so I can do it myself.

Explanation: I have a dir-locals flycheck setup that gets overwritten by auto-configure, so I鈥檇 like to exclude lsp-flycheck-enable, but keep the others (company, lsp-ui)

I think most reasonable would be to extract the three sections into separate functions. Thus I could just call the ones I need, with the added advantage that it would be easier to add more functionality to lsp--auto-configure when desired in the future.

@julianrubisch you can disable lsp-flycheck-enable call by setting the lsp-diagnostic-package to :none.

@julianrubisch you can disable lsp-flycheck-enable call by setting the lsp-diagnostic-package to :none.

Thanks, I just thought it could be made more obvious.

https://github.com/emacs-lsp/lsp-mode/pull/1607

Would it be possible to add the changes from this PR as the lsp-signature-function? That is, to have the signature in the echo field, not only when hovering, but also when typing out the arguments.

It might be possible without any changes in the source?

I notice lsp-typescript can install lsp server by itself if it hasn't found it.

I use rust-analyzer be my lsp server. However, lsp-mode won't install it. I wrote the common lisp script for installing rust-analyzer (I use it for updating, but it can install rust-analyzer too).

I am not very familiar with writing emacs mode. Do you guys wanna move this feature to emacs-lsp mode?

Thanks

@ccqpein thank you for pointing this out - we have this but to track RA installation - https://github.com/emacs-lsp/lsp-mode/issues/1400 . Generally, we would like to create an automatic installation for all language servers.

I whish to have full LSP snippet syntax support.
https://github.com/microsoft/language-server-protocol/blob/master/snippetSyntax.md

When enable lsp-snippet, sometimes expand to wrong content especially when it contains choice.

I whish to have full LSP snippet syntax support.
https://github.com/microsoft/language-server-protocol/blob/master/snippetSyntax.md

When enable lsp-snippet, sometimes expand to wrong content especially when it contains choice.

Definitely, I would call that a bug.

I wish we had a hook that runs before lsp is activated, the use case would be activating manually virtual environments in Python, for instance.

@shackra You could use lsp-before-initialize-hook.

I did not find it in the documentation, would it be possible with semantic highlighting to have items of the same kind coloured in different ways depending on the context?

For example, I would like to use a different color for variable declarations than for variable uses. It seems that the server I'm using supports this (rust-analyzer, as stated here) but I'm not sure if this is plain LSP or an extension.

Thanks for this great package!

@sebastiansturm do we support that?

in principle yes, though I haven't tested that part (if rust-analyzer supports modifiers, I'll do so this weekend). To enable it, you'd have to (setq lsp-semantic-tokens-apply-modifiers t), and you'll need to add face definitions for rust-analyzer's modifiers to lsp-semantic-token-modifier-faces (which we should then merge into lsp-mode proper, right now there's only a ;; TODO: add default definitions). Initially you might want to also (setq lsp-semantic-highlighting-warn-on-missing-face t) so you know exactly which face definitions you're missing

Hey @sebastiansturm, many thanks for your help with this!

I've applied the modifications you mentioned to my config, but it seems something may be missing because there is no change in how the code is highlighted.

image

When emacs starts there's also an error, though I'm not sure if it's really related (it was there since I enabled semantic highlighting, before using the modifiers)

image

Please tell me if I should open a different issue to avoid hijacking this one :)

I think a separate issue makes sense, unless the fix at https://github.com/sebastiansturm/lsp-mode is enough to resolve your issue. You'll still need to add the required face definitions to lsp-semantic-token-modifier-faces, though. Not sure about the error message; are you using multiple language servers with your rust buffer?

I can confirm that using your fork fixed my issue :tada: I can see now a different color for variable declarations.

About the error message, I've seen it only pops up when restoring a session (persp) with a rust buffer opened. I'll try to dig into it, but I consider my initial issue solved.

Thanks!!

can lsp-code-lens support helm like lsp-avy-lens ?

@thinkiny

can lsp-code-lens support helm like lsp-avy-lens ?

Can you describe how this will work? The challenge with helm interface is that we do not have enough context, e. g.

1 reference | 1 implementation
foo()
....

1 reference | 1 implementation
foo()

In this case, the only way to differentiate those two lenses in helm interface is to include the line number in helm and I am not 100% sure if this will be useful.

Any thoughts on adding a universal toggle for automatic import sorting on-save?

Lsp-java has its own lsp-java-save-actions-organize-imports toggle for this, but I think it would be nice to have for other language servers too. Obviously turning this setting on would only have an effect when the server supports it. This way instead of making custom before-save-hooks, users could simply tell lsp-mode to automatically organize imports on save (where possible).
Having this built into lsp-mode would probably help with issues that people may encounter when writing their own hooks for this (like this issue for example #1604).

Any thoughts on adding a universal toggle for automatic import sorting on-save?

Technically possible, but it would require a lot of work for relatively small gain.

What is the path forward for adding "arbitrary" commands defined by LS implementations? For example (and this is pretty arbitrary), https://rust-analyzer.github.io/manual.html#on-enter says you can add a snippet of config to your VSCode keybindings to enable this feature. What is the paradigm in lsp-mode? I had a brief look, based on lsp-rust-analyzer-join-lines and I learned that:

  • the params are defined by a macro in lsp-protocol.el
  • some code that populates the params

It seems to me (naively!) that I could just go ahead and figure out what params to pass, use lsp-interface to define that structure, and then write some code that wires it all up. And, potentially send a PR to pull it into the main codebase. Is that the recommended pattern?

It seems to me (naively!) that I could just go ahead and figure out what params to pass, use lsp-interface to define that structure, and then write some code that wires it all up. And, potentially send a PR to pull it into the main codebase. Is that the recommended pattern?

In this case, it seems like it does not need from a new lsp-interface since this extension point is using TextEdit and Position which are already present in the base protocol.

I think that it should be like this:

  1. Figure out how to handle RET key. There are at least 2 options - add an entry in rust-mode-map/rustic-mode-map to handle RET or use post-self-insert-hook
  2. Do the call against the server using lsp-request-async and apply the text edit.

@yyoncho Is it possible to add lsp-mode support for Jenkinsfile using this repo - https://github.com/sgwozdz/jenkinsfile-support

I have not worked Node ever so I do not know how to create a executable from this repo. I have already created a feature request with the owner - https://github.com/sgwozdz/jenkinsfile-support/issues/60.

But looks like the owner is not "active", can you please give me instructions or point me in the right direction to add jenkinsfile-support in lsp-mode?

FYI there is also a https://github.com/john2x/jenkinsfile-mode that I have contributed to, it just has company-mode and syntax highlighting support.

I suspect that you have to build this https://github.com/sgwozdz/jenkinsfile-support/tree/master/server via npm install and then use node <output-file> --stdio to start the server.

It would be nice if lsp-mode for typescript would utilize the project specific typescript binary located in node_modules/ if present. Either that or a .dir-locals.el safe variable to specify the typescript binary.

It would be nice if lsp-mode for typescript would utilize the project specific
typescript binary located in node_modules/ if present. Either that or a
.dir-locals.el safe variable to specify the typescript binary.

This will be possible after we restructure the order we find the server and select the root folder.

IMHO it would be convenient to manage LSP sessions from *lsp session* buffer (lsp-describe-session):

  1. Stop running servers.
    When I work on Java/Kotlin project, running two servers at the same time takes a lot of resources. Stopping servers from *lsp session* buffer would make resource management easier it these situations
  2. Jump to buffers
  3. Remove stopped sessions
    Sometimes bash-ls would create session in my /tmp directory or even in mounted volume. This would provide a way to remove these sessions from list

@matsievskiysv you may use this issue for your suggestions - https://github.com/emacs-lsp/lsp-mode/issues/764

Was this page helpful?
0 / 5 - 0 ratings

Related issues

axelson picture axelson  路  4Comments

dchneric picture dchneric  路  3Comments

rsuhada picture rsuhada  路  3Comments

dennismayr picture dennismayr  路  4Comments

alanz picture alanz  路  6Comments