;;; helm-eval.el --- eval expressions from helm. -*- lexical-binding: t -*- ;; Copyright (C) 2012 ~ 2020 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 . ;;; Code: (require 'cl-lib) (require 'helm) (require 'helm-help) (require 'eldoc) (require 'edebug) (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 "") 'helm-eval-new-line-and-indent) (define-key map (kbd "") 'lisp-indent-line) (define-key map (kbd "") '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 "") 'previous-line) (define-key map (kbd "") 'next-line) (define-key map (kbd "") 'forward-char) (define-key map (kbd "") '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))))) (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))) (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* ((sym (save-excursion (unless (looking-back ")\\|\"" (1- (point))) (forward-char -1)) (eldoc-current-symbol))) (info-fn (eldoc-fnsym-in-current-sexp)) (doc (or (eldoc-get-var-docstring sym) (eldoc-get-fnsym-args-string (car info-fn) (cadr info-fn))))) (when doc (funcall helm-eldoc-in-minibuffer-show-fn doc))))) (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-current-buffer helm-buffer (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") (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