216 lines
8.6 KiB
EmacsLisp
216 lines
8.6 KiB
EmacsLisp
;;; helm-eval.el --- eval expressions from helm. -*- lexical-binding: t -*-
|
||
|
||
;; Copyright (C) 2012 ~ 2021 Thierry Volpiatto
|
||
|
||
;; 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
|
||
;; the Free Software Foundation, either version 3 of the License, or
|
||
;; (at your option) any later version.
|
||
|
||
;; This program is distributed in the hope that it will be useful,
|
||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
;; GNU General Public License for more details.
|
||
|
||
;; You should have received a copy of the GNU General Public License
|
||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
||
;;; Code:
|
||
(require 'cl-lib)
|
||
(require 'helm)
|
||
(require 'helm-help)
|
||
(require 'eldoc)
|
||
(require 'edebug)
|
||
|
||
(declare-function helm-lisp-completion-at-point "helm-elisp.el")
|
||
|
||
|
||
(defgroup helm-eval nil
|
||
"Eval related Applications and libraries for Helm."
|
||
:group 'helm)
|
||
|
||
(defcustom helm-eldoc-in-minibuffer-show-fn
|
||
'helm-show-info-in-mode-line
|
||
"A function to display eldoc info.
|
||
Should take one arg: the string to display."
|
||
:group 'helm-eval
|
||
:type 'symbol)
|
||
|
||
(defcustom helm-show-info-in-mode-line-delay 12
|
||
"Eldoc will show info in mode-line during this delay if user is idle."
|
||
:type 'integer
|
||
:group 'helm-eval)
|
||
|
||
|
||
;;; Eldoc compatibility between emacs-24 and emacs-25
|
||
;;
|
||
(if (require 'elisp-mode nil t) ; emacs-25
|
||
;; Maybe the eldoc functions have been
|
||
;; already aliased by eldoc-eval.
|
||
(cl-loop for (f . a) in '((eldoc-current-symbol .
|
||
elisp--current-symbol)
|
||
(eldoc-fnsym-in-current-sexp .
|
||
elisp--fnsym-in-current-sexp)
|
||
(eldoc-get-fnsym-args-string .
|
||
elisp-get-fnsym-args-string)
|
||
(eldoc-get-var-docstring .
|
||
elisp-get-var-docstring))
|
||
unless (fboundp f)
|
||
do (defalias f a))
|
||
;; Emacs-24.
|
||
(declare-function eldoc-current-symbol "eldoc")
|
||
(declare-function eldoc-get-fnsym-args-string "eldoc" (sym &optional index))
|
||
(declare-function eldoc-get-var-docstring "eldoc" (sym))
|
||
(declare-function eldoc-fnsym-in-current-sexp "eldoc"))
|
||
|
||
;;; Evaluation Result
|
||
;;
|
||
;;
|
||
;; Internal
|
||
(defvar helm-eldoc-active-minibuffers-list nil)
|
||
|
||
(defvar helm-eval-expression-map
|
||
(let ((map (make-sparse-keymap)))
|
||
(set-keymap-parent map helm-map)
|
||
(define-key map (kbd "<C-return>") #'helm-eval-new-line-and-indent)
|
||
(define-key map (kbd "<M-tab>") #'lisp-indent-line)
|
||
(define-key map (kbd "<C-tab>") #'helm-lisp-completion-at-point)
|
||
(define-key map (kbd "C-p") #'previous-line)
|
||
(define-key map (kbd "C-n") #'next-line)
|
||
(define-key map (kbd "<up>") #'previous-line)
|
||
(define-key map (kbd "<down>") #'next-line)
|
||
(define-key map (kbd "<right>") #'forward-char)
|
||
(define-key map (kbd "<left>") #'backward-char)
|
||
map))
|
||
|
||
(defun helm-build-evaluation-result-source ()
|
||
(helm-build-dummy-source "Evaluation Result"
|
||
:multiline t
|
||
:mode-line "C-RET: nl-and-indent, M-tab: reindent, C-tab:complete, C-p/n: next/prec-line."
|
||
:filtered-candidate-transformer
|
||
(lambda (_candidates _source)
|
||
(list
|
||
(condition-case nil
|
||
(with-helm-current-buffer
|
||
(pp-to-string
|
||
(if edebug-active
|
||
(edebug-eval-expression
|
||
(read helm-pattern))
|
||
(eval (read helm-pattern) t))))
|
||
(error "Error"))))
|
||
:nohighlight t
|
||
:keymap helm-eval-expression-map
|
||
:action '(("Copy result to kill-ring" . (lambda (candidate)
|
||
(kill-new
|
||
(replace-regexp-in-string
|
||
"\n" "" candidate))
|
||
(message "Result copied to kill-ring")))
|
||
("copy sexp to kill-ring" . (lambda (_candidate)
|
||
(kill-new helm-input)
|
||
(message "Sexp copied to kill-ring"))))))
|
||
|
||
(defun helm-eval-new-line-and-indent ()
|
||
(interactive)
|
||
(newline) (lisp-indent-line))
|
||
|
||
(defun helm-eldoc-store-minibuffer ()
|
||
"Store minibuffer buffer name in `helm-eldoc-active-minibuffers-list'."
|
||
(with-selected-window (minibuffer-window)
|
||
(push (current-buffer) helm-eldoc-active-minibuffers-list)))
|
||
|
||
;; From emacs-28.1: As the eldoc API is nowaday a pain to use, try to
|
||
;; provide some eldoc in mode-line the best as possible (may break at
|
||
;; some point).
|
||
(defun helm-eldoc-show-in-eval ()
|
||
"Return eldoc in mode-line for current minibuffer input."
|
||
(let ((buf (window-buffer (active-minibuffer-window))))
|
||
(condition-case err
|
||
(when (member buf helm-eldoc-active-minibuffers-list)
|
||
(with-current-buffer buf
|
||
(let* ((info-fn (eldoc-fnsym-in-current-sexp))
|
||
(vsym (eldoc-current-symbol))
|
||
(sym (car info-fn))
|
||
(vardoc (eldoc-get-var-docstring vsym))
|
||
(doc (or vardoc
|
||
(eldoc-get-fnsym-args-string
|
||
sym (cadr info-fn))))
|
||
(all (format "%s: %s"
|
||
(propertize
|
||
(symbol-name (if vardoc vsym sym))
|
||
'face (if vardoc
|
||
'font-lock-variable-name-face
|
||
'font-lock-function-name-face))
|
||
doc)))
|
||
(when doc (funcall helm-eldoc-in-minibuffer-show-fn all)))))
|
||
(error (message "Eldoc in minibuffer error: %S" err) nil))))
|
||
|
||
(defun helm-show-info-in-mode-line (str)
|
||
"Display string STR in mode-line."
|
||
(save-selected-window
|
||
(with-helm-window
|
||
(let ((mode-line-format (concat " " str)))
|
||
(force-mode-line-update)
|
||
(sit-for helm-show-info-in-mode-line-delay))
|
||
(force-mode-line-update))))
|
||
|
||
;;; Calculation Result
|
||
;;
|
||
;;
|
||
(defvar helm-source-calculation-result
|
||
(helm-build-dummy-source "Calculation Result"
|
||
:filtered-candidate-transformer (lambda (_candidates _source)
|
||
(list
|
||
(condition-case err
|
||
(let ((result (calc-eval helm-pattern)))
|
||
(if (listp result)
|
||
(error "At pos %s: %s"
|
||
(car result) (cadr result))
|
||
result))
|
||
(error (cdr err)))))
|
||
:nohighlight t
|
||
:action '(("Copy result to kill-ring" . (lambda (candidate)
|
||
(kill-new candidate)
|
||
(message "Result \"%s\" copied to kill-ring"
|
||
candidate)))
|
||
("Copy operation to kill-ring" . (lambda (_candidate)
|
||
(kill-new helm-input)
|
||
(message "Calculation copied to kill-ring"))))))
|
||
|
||
;;;###autoload
|
||
(defun helm-eval-expression (arg)
|
||
"Preconfigured `helm' for `helm-source-evaluation-result'."
|
||
(interactive "P")
|
||
(let ((helm-elisp-help-function #'helm-elisp-show-doc-modeline))
|
||
(helm :sources (helm-build-evaluation-result-source)
|
||
:input (when arg (thing-at-point 'sexp))
|
||
:buffer "*helm eval*"
|
||
:echo-input-in-header-line nil
|
||
:history 'read-expression-history)))
|
||
|
||
(defvar eldoc-idle-delay)
|
||
;;;###autoload
|
||
(defun helm-eval-expression-with-eldoc ()
|
||
"Preconfigured `helm' for `helm-source-evaluation-result' with `eldoc' support."
|
||
(interactive)
|
||
(let ((timer (run-with-idle-timer
|
||
eldoc-idle-delay 'repeat
|
||
#'helm-eldoc-show-in-eval)))
|
||
(unwind-protect
|
||
(minibuffer-with-setup-hook
|
||
#'helm-eldoc-store-minibuffer
|
||
(call-interactively 'helm-eval-expression))
|
||
(and timer (cancel-timer timer))
|
||
(setq helm-eldoc-active-minibuffers-list
|
||
(cdr helm-eldoc-active-minibuffers-list)))))
|
||
|
||
;;;###autoload
|
||
(defun helm-calcul-expression ()
|
||
"Preconfigured `helm' for `helm-source-calculation-result'."
|
||
(interactive)
|
||
(helm :sources 'helm-source-calculation-result
|
||
:buffer "*helm calcul*"))
|
||
|
||
(provide 'helm-eval)
|
||
|
||
;;; helm-eval.el ends here
|