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

Add support for displaying various images #2248

Merged
merged 25 commits into from
Apr 28, 2018

Conversation

arrdem
Copy link
Contributor

@arrdem arrdem commented Mar 24, 2018

Fixes #1510

This is my second-pass changeset to implement support for embedding images in the REPL. It's probably got some sharp edges - my elisp isn't that good - but it definitely functions as advertised. I'd appreocate criticism on how the handlers should be made more configurable both on the nREPL side of the changeset (clojure-emacs/cider-nrepl#517) and on the emacs side.

Demo

2018-03-23-220235_1188x1542_scrot


Before submitting the PR make sure the following things have been done (and denote this
by checking the relevant checkboxes):

  • The commits are consistent with our contribution guidelines
  • You've added tests (if possible) to cover your change(s)
  • All tests are passing (make test)
  • All code passes the linter (make lint) which is based on elisp-lint and includes
  • You've updated the changelog (if adding/changing user-visible functionality)
  • You've updated the user manual (if adding/changing user-visible functionality)

Thanks!

If you're just starting out to hack on CIDER you might find this section of its
manual
extremely useful.

cider-repl.el Outdated
(lambda (buffer pprint-out)
(cider-repl-emit-result buffer pprint-out (not after-first-result-chunk))
(setq after-first-result-chunk t))
(lambda (buffer value content-type)
(if-let ((handler (cdr (assoc content-type cider-repl-content-type-handler-alist))))
Copy link
Member

@xiongtx xiongtx Mar 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if-let is obsolete. Emacs ≥26.1 uses if-let*, as does CIDER (via cider-compat.el).

@@ -764,7 +764,8 @@ to the REPL."
(defun nrepl-make-response-handler (buffer value-handler stdout-handler
stderr-handler done-handler
&optional eval-error-handler
pprint-out-handler)
pprint-out-handler
content-type-handler)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring should mention this and say what it does.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love it how ridiculous this function is at that point. :D It's certainly one of the prime candidates for some massive refactorings.

cider-repl.el Outdated
(create-image (if datap (base64-decode-string image) image) type datap
:margin cider-repl-image-margin))

(setq cider-repl-content-type-handler-alist
Copy link
Member

@xiongtx xiongtx Mar 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defvar/defcustom?

nrepl-client.el Outdated
(when (buffer-live-p buffer)
(with-current-buffer buffer
(when (and ns (not (derived-mode-p 'clojure-mode)))
(cider-set-buffer-ns ns))))
(cond (value
(cond ((and content content-type content-type-handler)
(funcall content-type-handler buffer content content-type))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

content-type-handler is called w/ only 3 args, while the functions in cider-repl-content-type-handler-alist can take 4.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. What happens here is that the content-type-handler function is called as (buff content content-type), it does its own internal dispatch, chooses a handler implementatiion (if any) and defers to that handler.

Because of the current mechanics of CIDER's various insertion functions, the arguments &optional show-prefix bol seem to be required in order to ensure that the user's prefix configuration is respected. The precise behavior of the bol flag is ... not obvious so I just retained it blindly.

I'd like to see the way that the REPL handles marks cleaned up to maintain one set of marks per eval requests which would I think simplify some of this logic and fix some interesting behavior where output / errors from threads forked by previous evals can become interleaved with the latest output and its value(s).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that'd be nice. Unfortunately there's a ton of legacy in the REPL code - much of it came straight from SLIME and was barely updated afterwards.

cider-repl.el Outdated
(cider-repl-emit-result buffer pprint-out (not after-first-result-chunk))
(setq after-first-result-chunk t))
(lambda (buffer value content-type)
(if-let ((handler (cdr (assoc content-type
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if-let -> if-let*

cider-repl.el Outdated
(create-image (if datap (base64-decode-string image) image) type datap
:margin cider-repl-image-margin))

(defun cider-repl-handle-jpeg (buffer image &optional show-prefix bol)
Copy link
Member

@xiongtx xiongtx Mar 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot of repetition w/ these functions. Seems like a cider-repl-handle-image that dispatches on image type could eliminate that. There's apply-partially if you need it.

cider-repl.el Outdated
("image/jpeg;base64" . #'cider-repl-handle-jpeg64)
("image/png". #'cider-repl-handle-png)
("image/png;base64" . #'cider-repl-handle-png64))
"Association list from content-types to handlers for inserting nREPL
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just alist is fine.

@stardiviner
Copy link
Contributor

stardiviner commented Mar 25, 2018

@arrdem in the screenshot, seems it is gnuplot to convert image. I'm watching this nREPL support image content-type topic. Because I want to add this feature to ob-clojure and ob-clojure-literate
But after reading you two post:

cider-repl.el Outdated
(set-marker cider-repl-prompt-start-mark (point) buffer))))
(cider-repl--show-maximum-output)))

(defcustom cider-repl-image-margin 10
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably we should also add some defcustom to turn this functionality off in case someone doesn't like it, or the initial implementation causes problems in some cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this. Should have an option to disable it to avoid early stage problems.

@bbatsov
Copy link
Member

bbatsov commented Mar 27, 2018

Overall things look good to me (apart from the failing build).

I'd appreocate criticism on how the handlers should be made more configurable both on the nREPL side of the changeset (clojure-emacs/cider-nrepl#517) and on the emacs side.

I guess one way to make things configurable would be set the configuration on the Emacs side and send it to the middleware as part of some "init" message when the connection is being established.

I dislike in the Emacs handlers that even though they don't have to support text, they current support params that make sense only for text output.

I've got two questions:

  1. What happens now if some expression produces some output and its result is an image.
  2. What happens if the resulting image can't fit in the REPL buffer (e.g. it's too wide)?

I'd be really nice to add support for this for interactive evaluation as well, which probably means some of the image handling code, shouldn't live in the REPL source code. E.g. you can show resulting images in an overlay or a dedicated result buffer, but obviously we can't show them in the minibuffer. :-)

Btw, I'm wondering about the special way you've treated value - is this really necessary? I can image we can both print the result and the image, although I guess that's a matter of personal preferences.

@stardiviner
Copy link
Contributor

About image too wide problem, I guess Emacs handle this. Org-mode has similar function to specify image with. I don't know how it is implemented. Even more, the image width can be smarter based on current Emacs window width.
Use code like this to get window width (window-width (selected-window) t)

@bbatsov
Copy link
Member

bbatsov commented Mar 28, 2018

One more thing - does clearing output in the REPL work properly for images? This certainly needs to be tested.

@bbatsov bbatsov merged commit dd83634 into clojure-emacs:master Apr 28, 2018
@zcaudate
Copy link
Contributor

This is really cool. Is is possible to get it working with cider-eval-last-sexp/cider-eval-last-sexp-to-repl?

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.

5 participants