Skip to content

add eldoc support #22

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

Merged
merged 2 commits into from
Nov 8, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,26 @@ result will be nothing short of havoc.**

`M-x inf-clojure` or `C-c C-z` within a Clojure source file.

## ElDoc

`eldoc-mode` is supported in Clojure source buffers and `*inferior-clojure*`
buffers which are running a Clojure REPL.

When ElDoc is enabled and there is an active REPL, it will show the
argument list of the function call you are currently editing in the
echo area.

You can activate ElDoc with `M-x eldoc-mode` or by adding the
following to you Emacs config:

```el
(add-hook 'clojure-mode-hook #'eldoc-mode)
(add-hook 'inf-clojure-mode-hook #'eldoc-mode)
```

ElDoc currently doesn't work with ClojureScript buffers and REPL's.
You can leave it enabled, it just won't show anything in the echo area.

## Troubleshooting

### REPL not responsive in Windows OS
Expand Down
107 changes: 102 additions & 5 deletions inf-clojure.el
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@

(require 'comint)
(require 'clojure-mode)
(require 'eldoc)
(require 'thingatpt)


(defgroup inf-clojure nil
Expand Down Expand Up @@ -131,6 +133,7 @@ The following commands are available:

\\{inf-clojure-minor-mode-map}"
:lighter "" :keymap inf-clojure-minor-mode-map
(inf-clojure-eldoc-setup)
(make-local-variable 'completion-at-point-functions)
(add-to-list 'completion-at-point-functions
#'inf-clojure-completion-at-point))
Expand Down Expand Up @@ -247,6 +250,7 @@ to continue it."
(setq comint-prompt-regexp inf-clojure-prompt)
(setq mode-line-process '(":%s"))
(clojure-mode-variables)
(inf-clojure-eldoc-setup)
(setq comint-get-old-input #'inf-clojure-get-old-input)
(setq comint-input-filter #'inf-clojure-input-filter)
(set (make-local-variable 'comint-prompt-read-only) inf-clojure-prompt-read-only)
Expand Down Expand Up @@ -429,11 +433,15 @@ Used by this command to determine defaults."
; doesn't need an exact name
(comint-check-source file-name) ; Check to see if buffer needs saved.
(setq inf-clojure-prev-l/c-dir/file (cons (file-name-directory file-name)
(file-name-nondirectory file-name)))
(file-name-nondirectory file-name)))
(comint-send-string (inf-clojure-proc)
(format inf-clojure-load-command file-name))
(inf-clojure-switch-to-repl t))

(defun inf-clojure-connected-p ()
"Return t if inferior Clojure is currently connected, nil otherwise."
(not (null inf-clojure-buffer)))


;;; Documentation functions: function doc, var doc, arglist, and
;;; describe symbol.
Expand Down Expand Up @@ -519,6 +527,9 @@ The value is nil if it can't find one."
(and (symbolp obj) obj)))
(error nil)))

(defun inf-clojure-symbol-at-point ()
"Return the name of the symbol at point, otherwise nil."
(or (thing-at-point 'symbol) ""))

;;; Documentation functions: var doc and arglist.
;;; ======================================================================
Expand All @@ -535,7 +546,7 @@ See variable `inf-clojure-var-source-command'."
(interactive (inf-clojure-symprompt "Var source" (inf-clojure-var-at-pt)))
(comint-proc-query (inf-clojure-proc) (format inf-clojure-var-source-command var)))

(defun inf-clojure-show-arglist (fn)
(defun inf-clojure-arglist (fn)
"Send a query to the inferior Clojure for the arglist for function FN.
See variable `inf-clojure-arglist-command'."
(interactive (inf-clojure-symprompt "Arglist" (inf-clojure-fn-called-at-pt)))
Expand All @@ -549,12 +560,16 @@ See variable `inf-clojure-arglist-command'."
(process-send-string proc eldoc-snippet)
(while (and (not (string-match inf-clojure-prompt kept))
(accept-process-output proc 2)))
; some nasty #_=> garbage appears in the output
(setq eldoc (and (string-match "(.+)" kept) (match-string 0 kept)))
)
(set-process-filter proc comint-filt))
(when eldoc
(message "%s: %s" fn eldoc))))
eldoc))

(defun inf-clojure-show-arglist (fn)
"Show the arglist for function FN in the mini-buffer."
(interactive (inf-clojure-symprompt "Arglist" (inf-clojure-fn-called-at-pt)))
(if-let ((eldoc (inf-clojure-arglist fn)))
(message "%s: %s" fn eldoc)))

(defun inf-clojure-show-ns-vars (ns)
"Send a query to the inferior Clojure for the public vars in NS.
Expand Down Expand Up @@ -644,6 +659,88 @@ Returns the selected completion or nil."
(completion-table-with-cache #'inf-clojure-completions)
(completion-table-dynamic #'inf-clojure-completions))))))

;;;; ElDoc
;;;; =====

(defvar inf-clojure-extra-eldoc-commands '("yas-expand")
"Extra commands to be added to eldoc's safe commands list.")

(defvar-local inf-clojure-eldoc-last-symbol nil
"The eldoc information for the last symbol we checked.")

(defun inf-clojure-eldoc-format-thing (thing)
"Format the eldoc THING."
(propertize thing 'face 'font-lock-function-name-face))

(defun inf-clojure-eldoc-beginning-of-sexp ()
"Move to the beginning of current sexp.

Return the number of nested sexp the point was over or after. "
(let ((parse-sexp-ignore-comments t)
(num-skipped-sexps 0))
(condition-case _
(progn
;; First account for the case the point is directly over a
;; beginning of a nested sexp.
(condition-case _
(let ((p (point)))
(forward-sexp -1)
(forward-sexp 1)
(when (< (point) p)
(setq num-skipped-sexps 1)))
(error))
(while
(let ((p (point)))
(forward-sexp -1)
(when (< (point) p)
(setq num-skipped-sexps (1+ num-skipped-sexps))))))
(error))
num-skipped-sexps))

(defun inf-clojure-eldoc-info-in-current-sexp ()
"Return a list of the current sexp and the current argument index."
(save-excursion
(let ((argument-index (1- (inf-clojure-eldoc-beginning-of-sexp))))
;; If we are at the beginning of function name, this will be -1.
(when (< argument-index 0)
(setq argument-index 0))
;; Don't do anything if current word is inside a string, vector,
;; hash or set literal.
(if (member (or (char-after (1- (point))) 0) '(?\" ?\{ ?\[))
nil
(list (inf-clojure-symbol-at-point) argument-index)))))

(defun inf-clojure-eldoc-arglist (thing)
"Return the arglist for THING."
(when (and thing
(not (string= thing ""))
(not (string-prefix-p ":" thing)))
;; check if we can used the cached eldoc info
(if (string= thing (car inf-clojure-eldoc-last-symbol))
(cdr inf-clojure-eldoc-last-symbol)
(let ((arglist (inf-clojure-arglist (substring-no-properties thing))))
(when arglist
(setq inf-clojure-eldoc-last-symbol (cons thing arglist))
arglist)))))

(defun inf-clojure-eldoc ()
"Backend function for eldoc to show argument list in the echo area."
(when (and (inf-clojure-connected-p)
;; don't clobber an error message in the minibuffer
(not (member last-command '(next-error previous-error))))
(let* ((info (inf-clojure-eldoc-info-in-current-sexp))
(thing (car info))
(value (inf-clojure-eldoc-arglist thing)))
(when value
(format "%s: %s"
(inf-clojure-eldoc-format-thing thing)
value)))))

(defun inf-clojure-eldoc-setup ()
"Turn on eldoc mode in the current buffer."
(setq-local eldoc-documentation-function #'inf-clojure-eldoc)
(apply #'eldoc-add-command inf-clojure-extra-eldoc-commands))

(provide 'inf-clojure)

;;; inf-clojure.el ends here