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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0ed1b73
Initial cut at image support
arrdem Mar 24, 2018
265e6e0
Use content to disambiguate from value
arrdem Mar 24, 2018
1508d28
Don't need to substring anymore
arrdem Mar 24, 2018
f7fecad
Get base64 decoding of images working
arrdem Mar 24, 2018
80038b3
Format
arrdem Mar 24, 2018
eb6f27e
Factor image insertion a bit better, add a customizable margin
arrdem Mar 24, 2018
0242edd
Re-indent for the linter
arrdem Mar 24, 2018
41efe47
Update CHANGELOG
arrdem Mar 24, 2018
9e209ef
Add to Author list
arrdem Mar 24, 2018
467192f
Add to Author list
arrdem Mar 24, 2018
3cb624f
if-let -> if-let*
arrdem Mar 24, 2018
58bbea5
Factor into defcustoms and some fns
arrdem Mar 24, 2018
bce72dc
Document the changes to nrepl-make-response-handler
arrdem Mar 24, 2018
0d7db17
Love to spend my time fixing format issues
arrdem Mar 26, 2018
a6c0993
Add :type for the linter
arrdem Mar 26, 2018
fb707f5
Add a content-type REPL option akin to the pprint option
arrdem Apr 12, 2018
0864d6d
Update the handler signature to reflect that content-type is now a 2-…
arrdem Apr 12, 2018
6baabf7
And working again atop 7a838de380f10acf813b1c22175a3f89a5a94956
arrdem Apr 28, 2018
964965e
Docstring
arrdem Apr 28, 2018
62bb7f0
Add another docstring
arrdem Apr 28, 2018
82c8f3b
Handle content-transfer-encoding magically
arrdem Apr 28, 2018
f37f51b
Don't manually manage base64 decoding anymore
arrdem Apr 28, 2018
e243028
Whitespace
arrdem Apr 28, 2018
6ca461e
Whitespace
arrdem Apr 28, 2018
e56ce60
More whitespace
arrdem Apr 28, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### New features

* [#2248](https://github.com/clojure-emacs/cider/pull/2248): `cider-repl` can now display recognized images in the REPL buffer.
* [#2172](https://github.com/clojure-emacs/cider/pull/2172): Render diffs for expected / actual test results.
* [#2167](https://github.com/clojure-emacs/cider/pull/2167): Add new defcustom `cider-jdk-src-paths`. Configure it to connect stack trace links to Java source code.
* [#2161](https://github.com/clojure-emacs/cider/issues/2161): Add new interactive command `cider-eval-defun-to-point` which is bound to `C-c C-v (C-)z`. It evaluates the current top-level form up to the point.
Expand Down
4 changes: 4 additions & 0 deletions cider-client.el
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,10 @@ result, and is included in the request if non-nil."
"pprint-fn" ,(or pprint-fn (cider--pprint-fn)))
(and right-margin `("print-right-margin" ,right-margin))))

(defun cider--nrepl-content-type-plist ()
"Plist to be appended to an eval request to make it use content-types."
'("content-type" "true"))

(defun cider-tooling-eval (input callback &optional ns)
"Send the request INPUT and register the CALLBACK as the response handler.
NS specifies the namespace in which to evaluate the request.
Expand Down
162 changes: 142 additions & 20 deletions cider-repl.el
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
;; Artur Malabarba <[email protected]>
;; Hugo Duncan <[email protected]>
;; Steve Purcell <[email protected]>
;; Reid McKenzie <[email protected]>

;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -112,7 +113,7 @@ If this is set to nil, no re-centering takes place."
:package-version '(cider . "0.11.0"))

(defcustom cider-repl-use-pretty-printing nil
"Control whether the results in REPL are pretty-printed or not.
"Control whether results in the REPL are pretty-printed or not.
The `cider-toggle-pretty-printing' command can be used to interactively
change the setting's value."
:type 'boolean
Expand All @@ -127,6 +128,13 @@ defaults to the variable `fill-column'."
:group 'cider-repl
:package-version '(cider . "0.15.0"))

(defcustom cider-repl-use-content-types t
"Control whether REPL results are presented using content-type information or not.
The `cider-toggle-content-types' command can be used to interactively
change the setting's value."
:type 'boolean
:group 'cider-repl)

(defcustom cider-repl-use-clojure-font-lock t
"Non-nil means to use Clojure mode font-locking for input and result.
Nil means that `cider-repl-input-face' and `cider-repl-result-face'
Expand Down Expand Up @@ -799,24 +807,133 @@ the symbol."
t)))
(t t))))

(defun cider-repl--display-image (buffer image &optional show-prefix bol string)
"Insert IMAGE into BUFFER at the current point.

For compatibility with the rest of CIDER's REPL machinery, supports
SHOW-PREFIX and BOL."
(with-current-buffer buffer
(save-excursion
(cider-save-marker cider-repl-output-start
(cider-save-marker cider-repl-output-end
(goto-char cider-repl-input-start-mark)
(when (and bol (not (bolp)))
(insert-before-markers "\n"))
(when show-prefix
(insert-before-markers
(propertize cider-repl-result-prefix 'font-lock-face 'font-lock-comment-face)))
(insert-image image string)
(set-marker cider-repl-input-start-mark (point) buffer)
(set-marker cider-repl-prompt-start-mark (point) buffer))))
(cider-repl--show-maximum-output))
t)

(defcustom cider-repl-image-margin 10
"Specifies the margin to be applied to images displayed in the REPL.

Either a single number of pixels - interpreted as a symmetric margin, or
pair of numbers `(x . y)' encoding an arbitrary margin."
:type '(choice integer (vector integer integer))
:group 'cider-repl
:package-version '(cider . "0.17.0"))

(defun cider-repl--image (data type datap)
"A helper for creating images with CIDER's image options.

FILE-OR-DATA is either the path to an image or its base64 coded data. TYPE
is a symbol indicating the image type. DATAP indicates whether the image is
the raw image data or a filename.

Returns an image instance with a margin per `cider-repl-image-margin'."
(create-image data type datap
:margin cider-repl-image-margin))

(defun cider-repl-handle-jpeg (_type buffer image &optional show-prefix bol)
"A handler for inserting a jpeg IMAGE into a repl BUFFER.
Part of the default `cider-repl-content-type-handler-alist'."
(cider-repl--display-image buffer
(cider-repl--image image 'jpeg t)
show-prefix bol image))

(defun cider-repl-handle-png (_type buffer image &optional show-prefix bol)
"A handler for inserting a png IMAGE into a repl BUFFER.
Part of the default `cider-repl-content-type-handler-alist'."
(cider-repl--display-image buffer
(cider-repl--image image 'png t)
show-prefix bol image))

(defun cider-repl-handle-external-body (type buffer _ &optional show-prefix bol)
"Handler for slurping external content into BUFFER.
Handles an external-body TYPE by issuing a slurp request to fetch the content."
(if-let* ((args (second type))
(access-type (nrepl-dict-get args "access-type")))
(nrepl-send-request
(list "op" "slurp" "url" (nrepl-dict-get args "URL"))
(cider-repl-handler buffer)
(cider-current-connection)))
nil)

(defcustom cider-repl-content-type-handler-alist
`(("message/external-body" . ,#'cider-repl-handle-external-body)
("image/jpeg" . ,#'cider-repl-handle-jpeg)
("image/png" . ,#'cider-repl-handle-png))
"Association list from content-types to handlers.

Handlers must be functions of two required and two optional arguments - the
REPL buffer to insert into, the value of the given content type as a raw
string, the REPL's show prefix as any and an `end-of-line' flag.

The return value of the handler should be a flag, indicating whether or not
the REPL is ready for a prompt to be displayed. Most handlers should return
`t', as the content-type response is (currently) an alternative to the
value response. However for handlers which themselves issue subsequent
nREPL ops, it may be convenient to prevent inserting a prompt."
:group 'cider-repl
:package-version '(cider . "0.17.0"))

(defun cider-repl-handler (buffer)
"Make an nREPL evaluation handler for the REPL BUFFER."
(nrepl-make-response-handler buffer
(let (after-first-result-chunk)
(lambda (buffer value)
(cider-repl-emit-result buffer value (not after-first-result-chunk) t)
(setq after-first-result-chunk t)))
(lambda (buffer out)
(cider-repl-emit-stdout buffer out))
(lambda (buffer err)
(cider-repl-emit-stderr buffer err))
(lambda (buffer)
(cider-repl-emit-prompt buffer))
nrepl-err-handler
(let (after-first-result-chunk)
(lambda (buffer pprint-out)
(cider-repl-emit-result buffer pprint-out (not after-first-result-chunk))
(setq after-first-result-chunk t)))))
(let (after-first-result-chunk
(show-prompt t))
(nrepl-make-response-handler
buffer
(lambda (buffer value)
(cider-repl-emit-result buffer value (not after-first-result-chunk) t)
(setq after-first-result-chunk t))
(lambda (buffer out)
(cider-repl-emit-stdout buffer out))
(lambda (buffer err)
(cider-repl-emit-stderr buffer err))
(lambda (buffer)
(when show-prompt
(cider-repl-emit-prompt buffer)
(let ((win (get-buffer-window (current-buffer) t)))
(when win
(with-selected-window win
(set-window-point win cider-repl-input-start-mark))
(cider-repl--show-maximum-output)))))
nrepl-err-handler
(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* ((content-attrs (second content-type))
(content-type* (first content-type))
(handler (cdr (assoc content-type*
cider-repl-content-type-handler-alist))))
(setq after-first-result-chunk t
show-prompt (funcall handler content-type buffer value
(not after-first-result-chunk) t))
(progn (cider-repl-emit-result buffer value (not after-first-result-chunk) t)
(setq after-first-result-chunk t)))))))

(defun cider--repl-request-plist (right-margin &optional pprint-fn)
"Plist to be appended to generic eval requests, as for the REPL.
PPRINT-FN and RIGHT-MARGIN are as in `cider--nrepl-pprint-request-plist'."
(nconc (when cider-repl-use-pretty-printing
(cider--nrepl-pprint-request-plist right-margin pprint-fn))
(when cider-repl-use-content-types
(cider--nrepl-content-type-plist))))

(defun cider-repl--send-input (&optional newline)
"Go to the end of the input and send the current input.
Expand Down Expand Up @@ -858,9 +975,7 @@ If NEWLINE is true then add a newline at the end of the input."
(cider-current-ns)
(line-number-at-pos input-start)
(cider-column-number-at-pos input-start)
(unless (or (not cider-repl-use-pretty-printing)
(string-match-p "\\`[ \t\r\n]*\\'" input))
(cider--nrepl-pprint-request-plist (cider--pretty-print-width))))))))
(cider--repl-request-plist (cider--pretty-print-width)))))))

(defun cider-repl-return (&optional end-of-input)
"Evaluate the current input string, or insert a newline.
Expand Down Expand Up @@ -936,6 +1051,13 @@ text property `cider-old-input'."
fill-column
80))

(defun cider-repl-toggle-content-types ()
"Toggle content-type rendering in the REPL."
(interactive)
(setq cider-repl-use-content-types (not cider-repl-use-content-types))
(message "Content-type support in REPL %s."
(if cider-repl-use-content-types "enabled" "disabled")))

(defun cider-repl-switch-to-other ()
"Switch between the Clojure and ClojureScript REPLs for the current project."
(interactive)
Expand Down
37 changes: 27 additions & 10 deletions nrepl-client.el
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
;; Artur Malabarba <[email protected]>
;; Hugo Duncan <[email protected]>
;; Steve Purcell <[email protected]>
;; Reid McKenzie <[email protected]>
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -764,7 +765,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.

"Make a response handler for connection BUFFER.
A handler is a function that takes one argument - response received from
the server process. The response is an alist that contains at least 'id'
Expand All @@ -773,20 +775,35 @@ and 'session' keys. Other standard response keys are 'value', 'out', 'err',

The presence of a particular key determines the type of the response. For
example, if 'value' key is present, the response is of type 'value', if
'out' key is present the response is 'stdout' etc. Depending on the type,
the handler dispatches the appropriate value to one of the supplied
handlers: VALUE-HANDLER, STDOUT-HANDLER, STDERR-HANDLER, DONE-HANDLER,
EVAL-ERROR-HANDLER, and PPRINT-OUT-HANDLER. If the optional
EVAL-ERROR-HANDLER is nil, the default `nrepl-err-handler' is used. If any
of the other supplied handlers are nil nothing happens for the
corresponding type of response."
'out' key is present the response is 'stdout' etc.

Depending on the type, the handler dispatches the appropriate value to one
of the supplied handlers: VALUE-HANDLER, STDOUT-HANDLER, STDERR-HANDLER,
DONE-HANDLER, EVAL-ERROR-HANDLER, PPRINT-OUT-HANDLER and
CONTENT-TYPE-HANDLER.

Handlers are functions of the buffer and the value they handle, except for
the optional CONTENT-TYPE-HANDLER which should be a function of the buffer,
content, the content-type to be handled as a list `(type attrs)'.

If the optional EVAL-ERROR-HANDLER is nil, the default `nrepl-err-handler'
is used. If any of the other supplied handlers are nil nothing happens for
the corresponding type of response."
(lambda (response)
(nrepl-dbind-response response (value ns out err status id pprint-out)
(nrepl-dbind-response response (content-type content-transfer-encoding body
value ns out err status id
pprint-out)
(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-type content-type-handler)
(funcall content-type-handler buffer
(if (string= content-transfer-encoding "base64")
(base64-decode-string body)
body)
content-type))
(value
(when value-handler
(funcall value-handler buffer value)))
(out
Expand Down