Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Truncate hovering information in echo area #111

Closed
wants to merge 2 commits into from

Conversation

fbergroth
Copy link
Contributor

Currently, hovering symbols with multiple lines of hover-info makes the echo area jump around a bit much.

I'm not sure if this should be customizable, we could check eldoc-echo-area-use-multiline-p, but I'd like this behavior only for hovering, and not for signatureHelp.

@joaotavora
Copy link
Owner

This appears to be related to #97. Both issues are on my radar, though I've been very busy lately. Thanks.

The string passed to eldoc-message needs to be truncated, otherwise
eldoc-last-message will be displayed with multiple lines.
@fbergroth
Copy link
Contributor Author

@joaotavora are you interested in this patch, or should I close it?

@joaotavora
Copy link
Owner

I don't know yet, but leave it open.

@fbergroth
Copy link
Contributor Author

Okay, so the current behavior keeps annoying me. I'm not sure if it's a problem with other servers, but with python I think it is.

When you hover a module, you get the entire docstring of the module printed in the echo area, which is sometimes rather long. If this docstring doesn't fit, you can only see the last part of it. When navigating through the code, the echo area jumps a lot, which is distracting.

If I want to see the entire documentation, I prefer to call eglot-help-at-point.

How about adding a variable

(defcustom eglot-hover-display-function #'eldoc-message)

And replacing (eldoc-message info) with (funcall eglot-hover-display-function info)?

Then I can have in my own config (or the function may be suitable to have in eglot)

(defun eglot-eldoc-message-first-line (text)
  (let* ((end (min (1- (window-width (minibuffer-window)))
                   (or (string-match-p "\n" text)
                       (length text))))
         (msg (substring text 0 end)))
    (eldoc-message msg)))

and (setq-local eglot-hover-display-function #'eglot-eldoc-message-first-line) in my python buffers.

It would also be easier to display hover information with tooltips, if some users prefer that.

@mkcms
Copy link
Collaborator

mkcms commented Dec 11, 2018

@fbergroth Take a look here, maybe it will be helpful. #112 (comment)

How about adding a variable

(defcustom eglot-hover-display-function #'eldoc-message)

Looks like there's interest in other display mechanisms. I personally have :hoverProvider disabled all the time. Maybe we can consider adding this variable, or customizing this behavior some other way.

@fbergroth
Copy link
Contributor Author

Thanks, that lead me to eldoc-message-function which I didn't know existed.

@fbergroth fbergroth closed this Dec 11, 2018
@fbergroth fbergroth deleted the truncate-hover branch December 11, 2018 08:27
@joaotavora
Copy link
Owner

Hi @fbergroth, sorry for being so late to the party. I had a look at the problem and this indeed as @mkcms says, it looks like something that could be made with eldoc-message-function. Also, instead of setting it globally you could/should use add-function:

(add-function :around (local 'eldoc-message-function)
              (lambda (original-fn &rest message-args)
                ;; massage message-args to cut off the bits that annoy you
                (apply original-fn message-args)))

Furthermore, you probably want to do this only when Eglot is active, right? For now you probably need to use eglot--managed-mode-hook, an internal hook, but see #182.

Also, can you post a screenshot or describe in some more detail exactly what the bother is with the jumping eldoc? Perhaps you could do something like @casouri does in https://github.com/casouri/eglot-doc, but simpler, and completely based on eldoc-message-function instead of a large extension that repeats some code in Eglot (though I have not tested that extension).

@fbergroth
Copy link
Contributor Author

Also, can you post a screenshot or describe in some more detail exactly what the bother is with the jumping eldoc?

Sure, here's a gif demonstrating the hovering docs being cut off. On a larger display, the echo area takes up a lot more space, and is appearing/disappearing ("jumping") a lot as you navigate through the code.

x

@fbergroth
Copy link
Contributor Author

Also, instead of setting it globally you could/should use add-function:

Really? eldoc-message-function is a variable. Isn't (setq-local eldoc-message-function my-eldoc-message-first-line) in i.e. python buffers more appropriate?

@joaotavora
Copy link
Owner

joaotavora commented Dec 11, 2018

Really? eldoc-message-function is a variable.

Yes really :-)

In lisp, we rather look at it as a symbol which has a variable binding to some value. In this case Emacs guarantees that type of that value is a function (named or unnamed, doesn't matter). So you can use add-function.

Try this in your python mode buffers

(add-function :around (local 'eldoc-message-function)
              (lambda (oldfun format-string &rest args)
                (if-let (frame (and
                                (string-match "\n" format-string)
                                (cdr (assoc "*eldoc frame*" (make-frame-names-alist)))))
                  (with-selected-frame frame
                    (delete-other-windows)
                    (with-current-buffer (get-buffer-create " *eldoc big doc*")
                      (erase-buffer)
                      (insert (apply #'format format-string args))
                      (switch-to-buffer (current-buffer))))
                  (apply oldfun args)))
              '((name . joaot/use-separate-frame-maybe)))

Won't do anything unless the frame exists, which you can make with (make-frame '((name . "*eldoc frame*")))

Isn't (setq-local eldoc-message-function my-eldoc-message-first-line) in i.e. python buffers more appropriate?

If you are using :around, it's just a stylistic difference I guess (though I prefer my style because it shows up in the C-h o of eldoc-message-function).

If you are using :before-until it's slightly more idiomatic:

(add-function :before-until (local 'eldoc-message-function)
              (lambda (format-string &rest args)
                (when-let (frame (and
                                (string-match "\n" format-string)
                                (cdr (assoc "*eldoc frame*" (make-frame-names-alist)))))
                  (with-selected-frame frame
                    (delete-other-windows)
                    (with-current-buffer (get-buffer-create " *eldoc big doc*")
                      (erase-buffer)
                      (save-excursion (insert (apply #'format format-string args)))
                      (switch-to-buffer (current-buffer))))))
              '((name . joaot/use-separate-frame-maybe)))

Anyway:

  • You should still decide what happens if you don't have the separate frame and the message is still too large (you could just ignore it).
  • This solution is completion eglot-independent, but you might want to do it in eglot--managed-mode-hook (adding and removing it after eglot stops managing).
  • But the same argument goes for making it global, especially since it would handle other eldoc "misbehavers", not just LSP-based ones. In that case just remove the local from the add-function.

@fbergroth
Copy link
Contributor Author

Thanks for the detailed answer. I learned something new :-)

Just to make it clear, I don't want a frame that displays documentation during hover. I would find that as distracting as eldoc being hammered with pages of text. I do think that eglot should support a lighter variant of hover documentation, instead of passing the entire thing (my suggestion is still to only display the first line with eldoc-message). But now I know how to fix this for myself, so I'll leave it there.

@joaotavora
Copy link
Owner

Thanks for the detailed answer. I learned something new :-)

You're welcome.

I do think that eglot should support a lighter variant of hover documentation, instead of passing the entire thing

But am I mistaken in thinking this is something you would want for any eldoc thing, and not necessarily related to eglot? If I'm not, then you should request that option of eldoc, i.e. Emacs itself. Use M-x report-emacs-bug for that, and put me in Cc.

@fbergroth
Copy link
Contributor Author

I've only encountered this in eglot, so I'm not sure if it applies elsewhere. Also as I mentioned in my first post, I only want this for hovering, not for signatureHelp, so some change would have to bemade in eglot. Maybe it's not worth fixing unless more people find it distracting.

@joaotavora
Copy link
Owner

I've only encountered this in eglot, so I'm not sure if it applies elsewhere.

By your description, it applies to every user of eldoc that uses it to issue longer-than-one-line messages.

for signatureHelp, so some change would have to bemade in eglot.

So if a signatureHelp is longer than one line that's OK?

Maybe it's not worth fixing unless more people find it distracting.

You can M-x report-emacs-bug anyway.

@fbergroth
Copy link
Contributor Author

By your description, it applies to every user of eldoc that uses it to issue longer-than-one-line messages.

I guess the modes that I've previously used only sent short text to eldoc, or it was customizable (i.e. anaconda-mode-eldoc-as-single-line).

So if a signatureHelp is longer than one line that's OK?

Yeah, for most cases I just see two lines here, the first being the function signature, and the latter information about the parameter I'm about to type. I think that's sensible.

@casouri
Copy link
Contributor

casouri commented Dec 11, 2018

I added a commit to eldoc-box(eglot-doc) so you can chose to leave one line messages in minibuffer and display multi-line messages in childframe. Basically that leaves function signatures in minibuffer and documentations in childframe. Maybe that's something you want?

casouri/eldoc-box@194ff41

@fbergroth
Copy link
Contributor Author

@casouri eldoc-box looks very nice! For some reason child frames causes my emacs to flicker, but I'll probably dig into why, since I'd like to use eldoc-box.

I don't think I'd use eldoc-box-only-multi-line though. I'd like to know ahead of time where text pops up, and where to look for it :-)

@joaotavora
Copy link
Owner

Yes, I tried it too. It's a very good first start. It's kind of unusably slow on Windows, but that's probably fixable.

Why does one have to close the new frame everytime?

There are some mentions of lsp there. Is all the code yours?

@casouri
Copy link
Contributor

casouri commented Dec 11, 2018

For some reason child frames causes my emacs to flicker

Does this fix it?

Why does one have to close the new frame every time?

That can be changed to make-frame-invisible, I'll try that.

There are some mentions of lsp there. Is all the code yours?

I went to lsp-ui-doc.el and copied a bunch of code. Is there anything I need to do?

@joaotavora
Copy link
Owner

That can be changed to make-frame-invisible, I'll try that.

Cool.

I went to lsp-ui-doc.el and copied a bunch of code. Is there anything I need to do?

Well IANAL, but there's a bit there at the top that says that the code is your copyright...

@casouri
Copy link
Contributor

casouri commented Dec 11, 2018

Oops! I'll remove that... That's embarrassing...

@joaotavora
Copy link
Owner

joaotavora commented Dec 11, 2018 via email

@casouri
Copy link
Contributor

casouri commented Dec 11, 2018

@fbergroth @joaotavora I've fixed the lagging and flicker problem and the copyright portion. It runs smoothly on my machine, could you give another try and see if it works?

There are another problem, though, if you have time and interest, could you have a look at casouri/eldoc-box#1 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants