├── .ert-runner
├── .gitignore
├── Cask
├── README.md
├── evalator-config.el
├── evalator-context.el
├── evalator-elisp.el
├── evalator-faces.el
├── evalator-history.el
├── evalator-key-map.el
├── evalator-state.el
├── evalator-utils.el
├── evalator.el
├── example-gifs
├── practical.gif
└── walkthrough.gif
└── test
├── evalator-config-test.el
├── evalator-context-test.el
├── evalator-elisp-test.el
├── evalator-history-test.el
├── evalator-state-test.el
├── evalator-test.el
├── evalator-utils-test.el
└── test-helper.el
/.ert-runner:
--------------------------------------------------------------------------------
1 | -L ./
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | test/helm
--------------------------------------------------------------------------------
/Cask:
--------------------------------------------------------------------------------
1 | (source gnu)
2 | (source melpa)
3 |
4 | (package-file "evalator.el")
5 |
6 | (files "*.el")
7 |
8 | (depends-on "helm-core" "1.9.1")
9 |
10 | (development
11 | (depends-on "f")
12 | (depends-on "ecukes")
13 | (depends-on "ert-runner")
14 | (depends-on "el-mock")
15 | (depends-on "noflet")
16 | )
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://melpa.org/#/evalator)
2 | [](http://stable.melpa.org/#/evalator)
3 |
4 |
5 | # EVALATOR #
6 |
7 | ## Introduction ##
8 |
9 | Evalator is a new kind of REPL for Emacs.
10 |
11 | What makes it different from a normal REPL? With evalator you can:
12 |
13 | * Evaluate expressions using any language*
14 | * See the results of an expression as it is typed
15 | * Transform data in a stepwise fashion
16 | * Generate code
17 | * Interact with Emacs using a language other than Elisp
18 |
19 | * Provided you've installed the appropriate extension package.
20 |
21 | ### Language Support ###
22 | By default, evalator uses Elisp as its evaluation language, but it
23 | supports other languages through extension packages. These packages
24 | define an evaluation context that can be passed to the ```evalator```
25 | command in order to set the evaluation language for that particular
26 | session.
27 |
28 | #### Currently available evaluation contexts ####
29 | * Clojure - via [evalator-clojure](https://github.com/seanirby/evalator-clojure)
30 |
31 | Don't see a context package for your favorite language? Write one!
32 | See
33 | [evalator-elisp.el](https://github.com/seanirby/evalator/blob/master/evalator-elisp.el)
34 | and
35 | [evalator-clojure.el](https://github.com/seanirby/evalator-clojure/blob/master/evalator-clojure.el)
36 | for examples. Open an issue in this repo with any questions and I'll
37 | be happy to help.
38 |
39 | Here's a practical example to demonstrate what evalator's capable of:
40 |
41 | ### A Practical Example ###
42 | Suppose you have several Emacs packages and want to generate an elisp
43 | configuration file for each one in your current directory. Each file
44 | should be named ```setup-$PACKAGE.el``` with ```$PACKAGE``` equaling
45 | the package name. Each file should contain the following line.
46 |
47 | ```
48 | (provide 'setup-$PACKAGE)
49 | ```
50 |
51 | Watch the example below to see how easy this is with evalator.
52 |
53 | 
54 |
55 | Here's what happened:
56 |
57 | * Copy all package names
58 | * Call ```M-x evalator``` to start an evalator session
59 | * Paste the package names into a lisp list ```'()```
60 | * Build a string for the shell command I want executed for each package
61 | * Call the Emacs command ```shell-command``` with each of the strings from before
62 | * Exit evalator using C-g
63 | * Verify the files are created with the right contents
64 |
65 | See the [Basic Usage](#basic-usage) section for a detailed
66 | walkthrough.
67 |
68 | ## Installation ##
69 |
70 | Evalator is available through the [MELPA](http://melpa.org/)
71 | repository. You can install it by using Emacs's built-in package
72 | manager ```package.el```.
73 |
74 | If you haven't configured ```package.el```, see
75 | [here](https://github.com/milkypostman/melpa#usage) for a short guide
76 | on setting it up to use MELPA.
77 |
78 | Once configured, you can run ```M-x package-install``` then enter
79 | ```evalator``` to install evalator.
80 |
81 | ## Setup ##
82 |
83 | ### Minimal ###
84 |
85 | Add the following to your init file to setup key bindings for the
86 | primary evalator commands.
87 |
88 | ```
89 | ;;Suggested keybindings
90 | (global-set-key (kbd "C-c e e") 'evalator)
91 | (global-set-key (kbd "C-c e x") 'evalator-explicit)
92 | (global-set-key (kbd "C-c e r") 'evalator-resume)
93 | (global-set-key (kbd "C-c e i") 'evalator-insert-equiv-expr)
94 | ```
95 |
96 | ### Auto Detect Context###
97 |
98 | If you've installed packages that provide other evalaution context's
99 | for evalator, you may want it to automatically detect what evalaution
100 | context to use. For example, if you're in a buffer using
101 | ```ruby-mode``` then you'd like your ```evalator``` session to use
102 | Ruby. Likewise, if you're in a buffer using ```clojure-mode``` then
103 | use Clojure.
104 |
105 | ```
106 | (setq evalator-config-mode-context-alist nil)
107 | (add-to-list 'evalator-config-mode-context-alist '(ruby-mode . evalator-ruby-context))
108 | (add-to-list 'evalator-config-mode-context-alist '(clojure-mode . evalator-clojure-context))
109 | ```
110 |
111 | ```evalator-config-mode-context-alist``` is an association list(alist) of
112 | pairs with the following form:
113 |
114 | ```'(MAJOR-MODE . CONTEXT)```
115 |
116 | ```evalator``` uses this alist in determining what context to use for
117 | a session. If a context wasn't passed explicity in a call to
118 | ```evalator``` or ```evalator-explicit```, then the current buffer's
119 | major-mode is searched for within the alist. If an entry is found, the
120 | context associated with that major-mode is used for the session.
121 | Otherwise an elisp evaluation context is used.
122 |
123 | ## Basic Usage ##
124 |
125 | 
126 |
127 | ### Basic Usage ###
128 |
129 | *Help is available anytime in an evalator session by running C-h
130 | m*. This will open a buffer containg help for evalator followed
131 | by help for helm.
132 |
133 | Start evalator by typing ```M-x evalator```. A helm buffer will be
134 | opened with a prompt to enter an expression. Enter a valid expression
135 | to generate the initial "candidates". A candidate is simply a result
136 | that is displayed. We call them candidates to stay consistent with
137 | helm, which evalator uses as a front-end. Once you've entered a valid
138 | expression press RET to confirm your expression.
139 |
140 | Evalator provides immediate feedback by attempting to evaluate your
141 | expression as it is typed. As soon as you've entered a valid
142 | expression the candidates are updated to show what their new value
143 | will be if you confirm your expression. Your expression prompt also
144 | will change color from red to green to indicate its validity.
145 |
146 | Obviously you can only confirm your expression when the expression is
147 | valid. If you try confirming an invalid expression the error message
148 | is displayed in the echo area.
149 |
150 | You can further transform the candidates you've generated by typing
151 | more expressions. Subsequent expressions are executed on each
152 | candidate. That is evalator's behavior in normal mode. You may
153 | refer to the current candidate by inserting the special arg character
154 | into your expression by pressing C-c ;.
155 |
156 | Modes and special args will be explained in more detail in the
157 | following sections.
158 |
159 | #### Modes ####
160 | You can start an evalator session with either ```M-x evalator``` or
161 | ```M-x evalator-explicit```. These commands start an evalator session
162 | in either normal mode or explicit mode, respectively.
163 |
164 | To explain the difference between these two modes, we first need to
165 | define what a "candidate" is. If evalator was started in normal mode
166 | (```M-x evalator```) and the result of an expression is an atomic
167 | value like an integer or string, then the result will be displayed as
168 | a single candidate. If the result is a collection like a list or
169 | vector, then each of the elements of that collection are displayed as
170 | a separate candidate.
171 |
172 | If evalator was started in explicit mode (```M-x evalator-explicit```)
173 | then the result is always displayed as a single candidate. One
174 | benefit to using explicit mode is that you can generate the equivalent
175 | code of a session using ```M-x evalator-insert-equiv-expr``` and
176 | insert that into a buffer*.
177 |
178 | So how do you choose what mode to use?
179 |
180 | Use normal mode if you want to transform collection data and you don't
181 | care about code generation.
182 |
183 | Use explicit mode otherwise.
184 |
185 | * The implementation of ```evalator-insert-equiv-expr``` is
186 | pretty naive currently so it's output may be sub-optimal if you used
187 | numbered special args like ```Ⓔ0``` in any of your expressions.
188 |
189 | #### Special Args ####
190 |
191 | When writing an expression in evalator, you use a special arg to refer
192 | to a particular candidate. You can easily insert this arg into the
193 | the expression prompt by pressing C-c ;, which is the key
194 | binding for ```evalator-action-insert-special-arg```. This arg is the
195 | character ```Ⓔ``` by default. If the last element was a collection
196 | then adding an integer N after the special arg, causes the Nth element
197 | from that candidate to be substituted.
198 |
199 | Here's what you would add to make the elisp context use ```@``` as
200 | its special arg:
201 |
202 | ```
203 | (setq evalator-elisp-special-arg "@")
204 | ```
205 |
206 | Or you could change the default special arg globally with the
207 | following:
208 | ```
209 | (setq evalator-context-special-arg-default "@")
210 | ```
211 |
212 | ### Public API ###
213 |
214 | The commands below can be bound to a shortcut or executed using ```M-x```
215 |
216 | ```evalator``` - Starts an evalator session
217 |
218 | ```evalator-explicit``` - Starts an evalator session in explicit mode
219 |
220 | ```evalator-resume``` - Resumes last evalator session
221 |
222 | ```evalator-insert-equiv-expr``` - Inserts the equivalent expression of the last evalator session into the current buffer. NOTE: The last session must have been run in explicit mode for this to work.
223 |
224 |
225 | ### Key Actions ###
226 | Below is a table of evalator specific key actions that can be used
227 | within the evalator session. For helm specific commands such as
228 | candidate navigation and marking, refer to
229 | [helm's](https://github.com/emacs-helm/helm) documentation or run
230 | C-h m to open a help buffer that contains evalator help
231 | followed by helm help.
232 |
233 | Shortcut | Command and Description
234 | ------------------------------- | -----------------------
235 | RET | ``uevalator-action-confirm-make-or-transformu``Accepts initial candidates or transformation then updates history. If transforming candidates, the expression is evaluated on each candidate with the special arg referring to the candidate's value
236 | C-c ; C-; | ```evalator-action-insert-special-arg```Insert's the current evaluation context's special arg.
237 | C-c C-c | ```evalator-action-confirm-transform-collection```Accepts transformation and updates history. Expression is evaluated once awith the special arg referring to the selected candidates list
238 | C-c C-e | ```evalator-action-execute-in-elisp```Executes the expression in Elisp on each selected candidate. The history is not updated and the candidates are not transformed
239 | C-j | ```evalator-action-next```Goes to next history state
240 | C-l | ```evalator-action-previous```Goes to previous history state
241 |
242 | You can exit the evalator session by quitting the minibuffer. This command is usually bound to C-g.
243 |
--------------------------------------------------------------------------------
/evalator-config.el:
--------------------------------------------------------------------------------
1 | ;;; evalator-config.el --- User configuration vars for evalator
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 | (defvar evalator-config-mode-context-alist nil
28 | "An association list that consists of (MAJOR-MODE . CONTEXT) pairs.
29 |
30 | When an evalator session is started, this list is used to determine
31 | the evaluation context to use. If the buffer's current major-mode
32 | matches a major-mode in this list, then the associated context will be
33 | used.
34 | ")
35 |
36 | (defvar evalator-config-prompt-f
37 | (lambda ()
38 | (format "%s of %s" (+ 1 (evalator-history-index)) (length (evalator-history))))
39 | "Var's value is a function used to generate the evalator prompt.")
40 |
41 | (provide 'evalator-config)
42 |
43 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
44 | ;;; evalator-config.el ends here
45 |
--------------------------------------------------------------------------------
/evalator-context.el:
--------------------------------------------------------------------------------
1 | ;;; evalator-context.el --- Definition for evalator-context class
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 |
28 | (require 'eieio)
29 | (require 'evalator-config)
30 |
31 | (defvar evalator-context-special-arg-default "Ⓔ") ;; Unicode character x24ba
32 |
33 | ;; References to data types in the docstrings below are assumed to be elisp types.
34 | (defclass evalator-context ()
35 | ((name
36 | :initarg :name
37 | :custom string
38 | :documentation
39 | "Name of the evaluation context (elisp, cider, etc..)")
40 |
41 | (special-arg
42 | :initarg :special-arg
43 | :custom string
44 | :documentation
45 | "Special arg used for substitution in evalator expressions.")
46 |
47 | (init
48 | :initarg :init
49 | :custom function
50 | :documentation
51 | "() => t or nil
52 |
53 | Performs any setup needed before any context evaluation functions
54 | are called. All slot functions below are context evaluation
55 | functions. If init returns non-nil then initialization was
56 | successful and evalator can be started. Otherwise, evalator is
57 | aborted. Function should print an error message if initialization
58 | failed.")
59 |
60 | (make-equiv-expr
61 | :initarg :make-equiv-expr
62 | :custom function
63 | :documentation
64 | "(exprs) => string
65 |
66 | This function accepts a single argument, EXPRS, which is the list
67 | of expression strings used in the most recent evalator session.
68 | It should combine them and return a single expression string as a
69 | result.")
70 |
71 | (make-candidates
72 | :initarg :make-candidates
73 | :custom function
74 | :documentation
75 | "(input mode initial-p) => cons
76 |
77 | Function converts INPUT into a valid list of helm candidates. In
78 | other words, a list of the stringified representation of the
79 | input. How INPUT is converted depends on both the MODE argument
80 | and the INITIAL-P flag.
81 |
82 | If INITIAL-P is non-nil then it is assumed that INPUT came from
83 | user input and first needs to be read and evaluated to an elisp
84 | object. If INITIAL-P is nil then it is treated as an elisp
85 | object. If MODE is :explicit then the function will always return
86 | a candidate list of one element. If MODE is some other value then
87 | the function will return a candidate list equivalent to the size
88 | of the input object. That means scalars will be returned in a
89 | size 1 candidates list. Vectors and lists will be returned in a
90 | candidates list whose size is equal to the size of the
91 | collection.")
92 |
93 | (transform-candidates
94 | :initarg :transform-candidates
95 | :custom function
96 | :documentation
97 | "(cands expr-str collect-p) => cons
98 |
99 | Function accepts a list of candidates, CANDS, and transforms it
100 | according to the expression string EXPR-STR. How CANDS is
101 | transformed depends on the flag COLLECT-P. If COLLECT-P is
102 | non-nil then EXPR-STR will be evaluated on the entire CANDS list.
103 | If COLLECT-P is nil then EXPR-STR will be evaluated on each
104 | candidate in CANDS. The result is then processed so it's a valid
105 | helm candidate list then returned.")))
106 |
107 | (defmethod evalator-context-get-special-arg ((context evalator-context))
108 | (or (eval (slot-value context :special-arg))
109 | evalator-context-special-arg-default))
110 |
111 | (defun evalator-context-get (&optional context)
112 | "Find an evaluation context to use for an evalator session.
113 |
114 | If CONTEXT is non-nil, then the result of calling CONTEXT's function
115 | definition will be used as the session's evaluation context.
116 |
117 | If CONTEXT is nil, then the current buffer's major mode will be
118 | searched for in `evalator-config-mode-context-alist'. If a match is found,
119 | the context associated with that major mode is used in the evalator
120 | session. If no match is found, an elisp evaluation context is used
121 | instead.
122 | "
123 | (let* ((mm (buffer-local-value 'major-mode (current-buffer)))
124 | (context-f-sym (or context
125 | (cdr (assoc mm evalator-config-mode-context-alist))
126 | 'evalator-elisp-context))
127 | (context-f (symbol-function context-f-sym)))
128 | (if (autoloadp context-f)
129 | (progn
130 | (autoload-do-load context-f context-f-sym)
131 | (funcall (symbol-function context-f-sym)))
132 | (funcall context-f))
133 | context-f-sym))
134 |
135 | (provide 'evalator-context)
136 |
137 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
138 | ;;; evalator-context.el ends here
139 |
--------------------------------------------------------------------------------
/evalator-elisp.el:
--------------------------------------------------------------------------------
1 | ;;; evalator-elisp.el --- Elisp evaluation context definition for Evalator
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 |
28 | (require 'cl-lib)
29 | (require 'eieio)
30 | (require 'evalator-context)
31 |
32 | (defvar evalator-elisp-special-arg nil)
33 | (defvar evalator-elisp-context nil)
34 |
35 | ;;;###autoload
36 | (defun evalator-elisp-context ()
37 | (if evalator-elisp-context
38 | evalator-elisp-context
39 | (progn
40 | (setq evalator-elisp-context
41 | (make-instance
42 | 'evalator-context
43 |
44 | :name
45 | "elisp"
46 |
47 | :special-arg
48 | 'evalator-elisp-special-arg
49 |
50 | :init
51 | 'evalator-elisp-init
52 |
53 | :make-equiv-expr
54 | 'evalator-elisp-make-equiv-expr
55 |
56 | :make-candidates
57 | 'evalator-elisp-make-candidates
58 |
59 |
60 | :transform-candidates
61 | 'evalator-elisp-transform-candidates)))))
62 |
63 | (defun evalator-elisp-get-special-arg ()
64 | "Return special arg from elisp context."
65 | (evalator-context-get-special-arg (evalator-elisp-context)))
66 |
67 | (defun evalator-elisp-init ()
68 | "No Elisp initialization needed since this is EMACS."
69 | t)
70 |
71 | ;; This function is gross because I can't use a regex matching function
72 | ;; I'd like to, but this function is called from within a regex match handler,
73 | ;; Calling one from within another produces unexpected behavior.
74 | (defun evalator-elisp-numbered-arg-num (str)
75 | "Function initially accepts a string STR of a numbered special arg.
76 | It is called recursively until it has extracted the number following
77 | the special arg."
78 | (let* ((first-char-str (cl-subseq str 0 1))
79 | (not-num-p (and (equal 0 (string-to-number first-char-str))
80 | (not (equal "0" first-char-str)))))
81 | (if not-num-p
82 | (evalator-elisp-numbered-arg-num (cl-subseq str 1))
83 | (string-to-number str))))
84 |
85 | (defun evalator-elisp-numbered-arg-pattern (&optional quote-p)
86 | "Return the regex pattern used to match numbered special args.
87 | If QUOTE-P is non-nil then a pattern is returned that can also match a
88 | quoted numbered special arg like `'ⒺN'."
89 | (let ((frmt (if quote-p "'?%s[0-9]+" "%s[0-9]+")))
90 | (format frmt (evalator-elisp-get-special-arg))))
91 |
92 | (defun evalator-elisp-subst-numbered-special-args (expr-str c)
93 | "Substitute any special args of the form `ⒺN' in EXPR-STR with the Nth element in C."
94 | (let ((pattern (evalator-elisp-numbered-arg-pattern))
95 | (match-f (lambda (m)
96 | (prin1-to-string (elt c (string-to-number (cl-subseq m 1)))))))
97 | (replace-regexp-in-string pattern match-f expr-str t t)))
98 |
99 | (defun evalator-elisp-subst-identity-special-args (expr-str c)
100 | "Substitute any special args of the form `Ⓔ' in EXPR-STR with C."
101 | (let ((sa (evalator-elisp-get-special-arg)))
102 | (replace-regexp-in-string sa (prin1-to-string c) expr-str t t)))
103 |
104 | (defun evalator-elisp-subst-special-args (expr-str c)
105 | "Substitute any special args in EXPR-STR.
106 | Identity special args like `Ⓔ' are substituted with the value of C.
107 | Numbered special args like `ⒺN' are substituted with the Nth element
108 | in C."
109 | (let* ((num-args-replaced (evalator-elisp-subst-numbered-special-args expr-str c))
110 | (num-and-identity-args-replaced
111 | (evalator-elisp-subst-identity-special-args num-args-replaced c)))
112 | num-and-identity-args-replaced))
113 |
114 | (defun evalator-elisp-eval (expr-str)
115 | "Evaluate the expression string, EXPR-STR."
116 | (eval (read expr-str)))
117 |
118 | (defun evalator-elisp-make-equiv-expr (exprs)
119 | "See slot documentation in evalator-context.el."
120 | (let* ((pattern-numbered (evalator-elisp-numbered-arg-pattern t))
121 | (pattern-identity (format "'?%s" (evalator-elisp-get-special-arg)))
122 | (match-f (lambda (str m)
123 | (concat "(elt "
124 | str
125 | " "
126 | (number-to-string (evalator-elisp-numbered-arg-num m))
127 | ")")))
128 | (sub (lambda (expr-acc expr)
129 | (let* ((match-f (apply-partially match-f expr-acc))
130 |
131 | (num-args-replaced
132 | (replace-regexp-in-string pattern-numbered match-f expr t))
133 |
134 | (num-and-identity-args-replaced
135 | (replace-regexp-in-string pattern-identity expr-acc num-args-replaced t t)))
136 | num-and-identity-args-replaced))))
137 | (cl-reduce sub exprs)))
138 |
139 | (defun evalator-elisp-make-candidates (input mode)
140 | "See slot documentation in evalator-context.el."
141 | (let* ((data (eval (read input)))
142 | (to-obj-string (lambda (x)
143 | (prin1-to-string x))))
144 | (cond
145 | ((equal :explicit mode) (list (funcall to-obj-string data)))
146 | ((and (not (stringp data)) (sequencep data)) (mapcar to-obj-string data))
147 | (t (list (funcall to-obj-string data))))))
148 |
149 | (defun evalator-elisp-transform-candidates (cands expr-str collect-p)
150 | "See slot documentation in evalator-context.el."
151 | (let ((cands-xfrmd (if collect-p
152 | (list (evalator-elisp-eval
153 | (evalator-elisp-subst-special-args
154 | expr-str (mapcar 'read cands))))
155 | (mapcar (lambda (c)
156 | (evalator-elisp-eval
157 | (evalator-elisp-subst-special-args
158 | expr-str (read c)))) cands))))
159 | (mapcar `prin1-to-string cands-xfrmd)))
160 |
161 | (provide 'evalator-elisp)
162 |
163 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
164 | ;;; evalator-elisp.el ends here
165 |
--------------------------------------------------------------------------------
/evalator-faces.el:
--------------------------------------------------------------------------------
1 | ;;; evalator-faces.el --- Evalator faces
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 |
28 | (defgroup evalator-faces nil
29 | "Customize the appearance of evalator."
30 | :prefix "evalator-"
31 | :group 'faces
32 | :group 'evalator)
33 |
34 | (defface evalator-success
35 | '((((background dark))
36 | :background "green4"
37 | :foreground "white"
38 | )
39 | (((background light))
40 | :background "green4"
41 | :foreground "white"
42 | ))
43 | "Face for source header in the evalator buffer."
44 | :group 'evalator-faces)
45 |
46 | (defface evalator-error
47 | '((((background dark))
48 | :background "red4"
49 | :foreground "white"
50 | )
51 | (((background light))
52 | :background "red4"
53 | :foreground "white"
54 | ))
55 | "Face for source header in the evalator buffer."
56 | :group 'evalator-faces)
57 |
58 | (provide 'evalator-faces)
59 |
60 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
61 | ;;; evalator-faces.el ends here
62 |
--------------------------------------------------------------------------------
/evalator-history.el:
--------------------------------------------------------------------------------
1 | ;;; evalator-history.el --- History helpers
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 |
28 | (require 'cl-lib)
29 | (require 'evalator-utils)
30 | (require 'evalator-state)
31 | (require 'helm)
32 |
33 | (defun evalator-history ()
34 | "Return history vector."
35 | (plist-get evalator-state :history))
36 |
37 | (defun evalator-history-index ()
38 | "Return current history index."
39 | (plist-get evalator-state :history-index))
40 |
41 | (defun evalator-history-push! (candidates expression)
42 | "Push the CANDIDATES and EXPRESSION onto history.
43 | Increments the history index."
44 | (evalator-utils-put! evalator-state
45 | :history
46 | (vconcat (cl-subseq (evalator-history) 0 (+ 1 (evalator-history-index)))
47 | (list (list :candidates candidates :expression expression))))
48 | (evalator-utils-put! evalator-state :history-index (+ 1 (evalator-history-index))))
49 |
50 | (defun evalator-history-current (&optional k)
51 | "Retrieve active history element. Accepts an optional keyword K."
52 | (let ((h (elt (evalator-history) (evalator-history-index))))
53 | (if k (plist-get h k) h)))
54 |
55 | (defun evalator-history-expression-chain ()
56 | "Return a list of all expressions in history except for the first.
57 | The first expression is always an empty string so it is ignored."
58 | (cdr (mapcar
59 | (lambda (h)
60 | (plist-get h :expression))
61 | (plist-get evalator-state :history))))
62 |
63 | (provide 'evalator-history)
64 |
65 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
66 | ;;; evalator-history.el ends here
67 |
--------------------------------------------------------------------------------
/evalator-key-map.el:
--------------------------------------------------------------------------------
1 | ;;; evalator-key-map.el --- Key map for evalator actions
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 |
28 | (require 'helm)
29 |
30 | (defvar evalator-key-map
31 | (let ((map (make-sparse-keymap)))
32 | (define-key map (kbd "RET") 'evalator-action-confirm-make-or-transform)
33 | (define-key map (kbd "C-c ;") 'evalator-action-insert-special-arg)
34 | (define-key map (kbd "C-;") 'evalator-action-insert-special-arg)
35 | (define-key map (kbd "C-c C-c") 'evalator-action-confirm-transform-collect)
36 | (define-key map (kbd "C-c C-e") 'evalator-action-execute-in-elisp)
37 | (define-key map (kbd "C-j") 'evalator-action-next)
38 | (define-key map (kbd "C-l") 'evalator-action-previous)
39 | map))
40 |
41 | (provide 'evalator-key-map)
42 |
43 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
44 | ;;; evalator-key-map.el ends here
45 |
--------------------------------------------------------------------------------
/evalator-state.el:
--------------------------------------------------------------------------------
1 | ;;; evalator-state.el --- evalator-state var and helpers
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 |
28 | (require 'helm)
29 | (require 'evalator-utils)
30 | (require 'evalator-context)
31 | (require 'evalator-elisp)
32 |
33 | (defvar evalator-state-default (list :context 'evalator-elisp-context
34 | :mode :normal
35 | :history []
36 | :history-index -1))
37 |
38 | (defvar evalator-state (copy-sequence evalator-state-default))
39 |
40 | (defun evalator-state-init (&optional mode context)
41 | "Initialize the `evalator-state' var.
42 |
43 | First, `evalator-state-default' is copied to `evalator-state'.
44 | Then, the state's `:mode' is set to MODE if MODE is non-nil.
45 | Finally the function defined in the context's `:init' slot is called
46 | to perform any context specific initialization.
47 | "
48 | (evalator-elisp-context) ;;Initialize elisp context if not already.
49 | (setq evalator-state (copy-sequence evalator-state-default))
50 | (evalator-utils-put! evalator-state :context (evalator-context-get context))
51 | (when mode
52 | (evalator-utils-put! evalator-state :mode mode))
53 | (funcall (slot-value (evalator-state-context) :init)))
54 |
55 | (defun evalator-state-context ()
56 | "Return the state's context object."
57 | (funcall (plist-get evalator-state :context)))
58 |
59 | (provide 'evalator-state)
60 |
61 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
62 | ;;; evalator-state.el ends here
63 |
--------------------------------------------------------------------------------
/evalator-utils.el:
--------------------------------------------------------------------------------
1 | ;;; evalator-utils.el --- Utilities for evalator requiring no dependencies
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 |
28 | (defun evalator-utils-put! (plst p v)
29 | "Set PLST property P with value V."
30 | (setq plst (plist-put plst p v)))
31 |
32 | (defun evalator-utils-get-file-string (filepath)
33 | "Return contents of file at FILEPATH."
34 | (with-temp-buffer
35 | (insert-file-contents filepath)
36 | (buffer-string)))
37 |
38 | (provide 'evalator-utils)
39 |
40 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
41 | ;;; evalator-utils.el ends here
42 |
--------------------------------------------------------------------------------
/evalator.el:
--------------------------------------------------------------------------------
1 | ;;; evalator.el --- Package for interactive transformation of data with helm
2 | ;;
3 | ;; Copyright © , Sean Irby
4 | ;; Author: Sean Irby
5 | ;; Maintainer: Sean Irby
6 | ;; URL: http://www.github.com/seanirby/evalator
7 | ;; Version: 1.0.0
8 | ;; Keywords: languages, elisp, helm
9 | ;; Package-Requires: ((helm-core "1.9.1"))
10 | ;;
11 | ;; This program is free software: you can redistribute it and/or modify
12 | ;; it under the terms of the GNU General Public License as published by
13 | ;; the Free Software Foundation, either version 3 of the License, or (at
14 | ;; your option) any later version.
15 | ;;
16 | ;; This program is distributed in the hope that it will be useful, but
17 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 | ;; General Public License for more details.
20 | ;;
21 | ;; You should have received a copy of the GNU General Public License
22 | ;; along with GNU Emacs. If not, see .
23 | ;;
24 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
25 | ;;
26 | ;;; This file is not a part of GNU Emacs
27 | ;;
28 | ;;; Commentary:
29 | ;;
30 | ;;; Code:
31 |
32 | (require 'cl-lib)
33 | (require 'evalator-config)
34 | (require 'evalator-context)
35 | (require 'evalator-elisp)
36 | (require 'evalator-faces)
37 | (require 'evalator-history)
38 | (require 'evalator-key-map)
39 | (require 'evalator-state)
40 | (require 'evalator-utils)
41 | (require 'helm)
42 |
43 | (defvar evalator-help-message
44 | "* Evalator Help
45 |
46 | Evalator is a new kind of REPL for Emacs. It lets you interactively
47 | transform data and can support many different languages.
48 |
49 | Since the evalator documentation is in a state of flux currently, this
50 | help will only reference things that aren't likely to change.
51 |
52 | NOTE: This help also includes the help for helm, which evalator depends
53 | on as a front-end. Knowing some basic helm commands is helpful to using
54 | evalator to its full potential. The generic helm help is located directly
55 | under this help section.
56 |
57 | ** Evalator Session Shortcuts
58 |
59 | The shortcuts in the table below are used while in the evalator buffer.
60 |
61 | |----------+-------------------------------------------+------------------------------------------|
62 | | Shortcut | Command | Description |
63 | |----------+-------------------------------------------+------------------------------------------|
64 | | | | |
65 | | RETURN | evalator-action-confirm-make-or-transform | Confirm the expression and either |
66 | | | | make the initial candidates or transform |
67 | | | | the existing ones. |
68 | | | | |
69 | | | | If this action is executed at the first |
70 | | | | evalator prompt then confirming will |
71 | | | | generate the initial candidates. |
72 | | | | |
73 | | | | Otherwise, confirmation evaluates the |
74 | | | | expression on each candidate. |
75 | | | | |
76 | |----------+-------------------------------------------+------------------------------------------|
77 | | | | |
78 | | C-c ; | evalator-action-insert-special-arg | Insert the special arg character |
79 | | | | |
80 | | or | | See the \"Special Args\" section below |
81 | | | | for details on using special args. |
82 | | C-; | | |
83 | | | | |
84 | |----------+-------------------------------------------+------------------------------------------|
85 | | | | |
86 | | C-c C-c | evalator-action-confirm-transform-collect | This command causes the candidates to |
87 | | | | be treated as a single collection. |
88 | | | | |
89 | | | | This means that the special arg will |
90 | | | | refer to the entire collection of |
91 | | | | candidates. The expression will only be |
92 | | | | evaluated on this collection and only |
93 | | | | one candidate will be produced as a |
94 | | | | result. |
95 | | | | |
96 | | | | This command is useful for generating |
97 | | | | an aggregate result. |
98 | | | | |
99 | |----------+-------------------------------------------+------------------------------------------|
100 | | | | |
101 | | C-c C-e | evalator-action-execute-in-elisp | Execute the expression on each candidate |
102 | | | | using Elisp. |
103 | | | | |
104 | | | | NOTE: This action does not transform the |
105 | | | | candidates |
106 | | | | |
107 | |----------+-------------------------------------------+------------------------------------------|
108 | | | | |
109 | | C-j | evalator-action-next | Go to next history state |
110 | | | | |
111 | |----------+-------------------------------------------+------------------------------------------|
112 | | | | |
113 | | C-l | evalator-action-previous | Go to previous history state |
114 | | | | |
115 | |----------+-------------------------------------------+------------------------------------------|
116 |
117 | ** Global commands
118 |
119 | Below are the global evalator commands that can be run using M-x.
120 |
121 | You should probably bind them to shortcut.
122 |
123 | |----------------------------+-----------------------------------------------------------------------------------|
124 | | Command | Description |
125 | |----------------------------+-----------------------------------------------------------------------------------|
126 | | | |
127 | | evalator | Starts an evalator session. |
128 | | | |
129 | | | Command accepts an optional mode and evaluation context as arguments. |
130 | | | |
131 | | | If the mode arg is nil, then normal mode is used. |
132 | | | |
133 | | | If the evaluation context arg is nil, then the felisp evaluation context is used. |
134 | | | |
135 | |----------------------------+-----------------------------------------------------------------------------------|
136 | | | |
137 | | evalator-explicit | Starts an evalator session in explicit mode. |
138 | | | |
139 | | | Accepts an optional evaluation context |
140 | | | |
141 | |----------------------------+-----------------------------------------------------------------------------------|
142 | | | |
143 | | evalator-resume | Resumes your last evalator session |
144 | | | |
145 | |----------------------------+-----------------------------------------------------------------------------------|
146 | | | |
147 | | evalator-insert-equiv-expr | Inserts the equivalent expression of the last evalator session into the |
148 | | | current buffer. |
149 | | | |
150 | | | NOTE: The last session must have been run in explicit mode for this to work. |
151 | | | |
152 | |----------------------------+-----------------------------------------------------------------------------------|
153 |
154 | ** Special Args
155 |
156 | Special arguments are used to refer to the value of the candidate being
157 | transformed. The default special character is Ⓔ which is the unicode
158 | character x24ba. If a candidate was equal to the number 1 and your
159 | expression was (+ 1 Ⓔ) then 1 would be substituted for the special arg
160 | and the result would evaluate to 2.
161 |
162 | You can insert the special arg into the expression prompt by using the
163 | shortcut C-c ; which executes `evalator-action-insert-special-arg'.
164 | An abbreviated shortcut C-; has been provided for GUI Emacs users has
165 |
166 | If the candidate is a collection you can refer to an element within it
167 | by adding an integer after the special arg. So if a candidate is the
168 | vector [1 2 3 4] and your expression is (+ 1 Ⓔ0) then Ⓔ0 would be
169 | substituted with 1 and the result would evaluate to 2.
170 |
171 | ")
172 |
173 | (defvar evalator-expression-prompt "Enter Expression: ")
174 | (defvar evalator-candidates-initial '("Enter an expression below to generate initial data")
175 | "Informational candidate used on evalator startup.")
176 |
177 | (defvar evalator-error nil
178 | "Error symbol used for signaling evalator errors.")
179 | (put 'evalator-error 'error-conditions '(error))
180 |
181 | (defun evalator-action-previous ()
182 | "Go to the previous state and update the evalator session."
183 | (interactive)
184 | (when (not (equal 0 (evalator-history-index)))
185 | (evalator-utils-put! evalator-state :history-index (+ -1 (evalator-history-index)))
186 | (helm-unmark-all)
187 | (helm-set-pattern "")
188 | (helm-update)))
189 |
190 | (defun evalator-action-next ()
191 | "Go to the next history state and update the evalator session."
192 | (interactive)
193 | (when (not (equal (+ -1 (length (evalator-history))) (evalator-history-index)))
194 | (evalator-utils-put! evalator-state :history-index (+ 1 (evalator-history-index)))
195 | (helm-unmark-all)
196 | (helm-set-pattern "")
197 | (helm-update)))
198 |
199 | (defun evalator-action-execute-in-elisp ()
200 | "Execute expression in elisp context.
201 | This function is useful if you want to execute an Emacs command or
202 | Elisp function from within an evalator session that uses a different
203 | evaluation context. This action does not transform the candidates."
204 | (interactive)
205 | (let* ((spec-arg-elisp (evalator-context-get-special-arg (evalator-elisp-context)))
206 | (spec-arg-curr (evalator-context-get-special-arg (evalator-state-context)))
207 | (expr-str (if (equal spec-arg-elisp spec-arg-curr)
208 | helm-pattern
209 | (replace-regexp-in-string spec-arg-curr spec-arg-elisp helm-pattern))))
210 | (condition-case err
211 | (message
212 | (prin1-to-string
213 | (evalator-elisp-transform-candidates (evalator-get-candidates) expr-str nil)))
214 | (error
215 | (evalator-message err)))))
216 |
217 | (defun evalator-action-confirm-make-or-transform (&optional f-and-args)
218 | "Make initial candidates or transform candidates then update history.
219 | Accepts an optional arg F-AND-ARGS to pass to `evalator-make-or-transform'."
220 | (interactive)
221 | (let* ((err-handler (lambda (err-str)
222 | (evalator-message (concat "Error: " err-str))
223 | nil))
224 | (cands (evalator-candidate-make-or-transform f-and-args err-handler)))
225 | (when cands
226 | (evalator-history-push! cands helm-pattern)
227 | (helm-unmark-all)
228 | (helm-set-pattern ""))))
229 |
230 | (defun evalator-action-confirm-transform-collect ()
231 | "Transform the entire candidate selection then update history.
232 | Normally candidates are transformed by evaluating the current
233 | expression on each candidate with the special arg referring to the
234 | value of the candidate. This action changes that behavior in two
235 | ways. First, it changes the meaning of the special arg so it refers
236 | to the entire candidate selection. Second, the current expression is
237 | evaluated only once to produce a single candidate. This action is
238 | used for when you need to produce an aggregate result."
239 | (interactive)
240 | (let* ((f (slot-value (evalator-state-context) :transform-candidates))
241 | (expr-str helm-pattern)
242 | (args (list (evalator-get-candidates) expr-str t)))
243 | (evalator-action-confirm-make-or-transform (list f args))))
244 |
245 | (defun evalator-action-insert-special-arg ()
246 | "Insert the evalator special arg into the expression prompt."
247 | (interactive)
248 | (insert (evalator-context-get-special-arg (evalator-state-context))))
249 |
250 | (defun evalator-message (msg)
251 | "Output MSG and append a newline and an instruction to continue."
252 | (read-char (concat msg "\n" "Press any key to return to minibuffer."))
253 | ;; Hack needed because minibuffer window doesn't resize after printing a multiline message
254 | (with-current-buffer (window-buffer (active-minibuffer-window))
255 | (let ((txt (minibuffer-contents)))
256 | (delete-minibuffer-contents)
257 | (insert txt))))
258 |
259 | (defun evalator-flash (status)
260 | "Change the evalator expression prompt face according to STATUS."
261 | (let ((f (if (equal :success status) 'evalator-success 'evalator-error)))
262 | (with-current-buffer (window-buffer (active-minibuffer-window))
263 | (face-remap-add-relative 'minibuffer-prompt f))))
264 |
265 | (cl-defun evalator-marked-candidates (&key with-wildcard)
266 | "Same as 'helm-marked-candidates' except it returns nil if no candidates were marked."
267 | (with-current-buffer helm-buffer
268 | (let ((candidates
269 | (cl-loop with current-src = (helm-get-current-source)
270 | for (source . real) in (reverse helm-marked-candidates)
271 | when (equal (assq 'name source) (assq 'name current-src))
272 | append (helm--compute-marked real source with-wildcard)
273 | into cands
274 | finally return cands)))
275 | candidates)))
276 |
277 | (defun evalator-persistent-help ()
278 | "Return persistent help string."
279 | (cl-flet ((f (command)
280 | (key-description (where-is-internal command evalator-key-map t))))
281 | (concat "History forward, "
282 | (f 'evalator-action-previous) ": History backward, "
283 | (f 'evalator-action-confirm-make-or-transform) ": Accept transformation, "
284 | (f 'evalator-action-insert-special-arg) ": Insert special arg")))
285 |
286 | (defun evalator-get-candidates ()
287 | "Return current evalator candidates.
288 | If there are marked candidates, return the list of those. Otherwise,
289 | return a list of all the candidates."
290 | (let ((cands-all (evalator-history-current :candidates))
291 | (cands-marked (evalator-marked-candidates)))
292 | (or cands-marked cands-all)))
293 |
294 | (defun evalator-try-context-f (context-f args &optional err-handler)
295 | "Try executing the given evaluation context function CONTEXT-F.
296 | Calls TRANSFORM-F with the given ARGS. Returns the result if the
297 | operation was successful. If there was an error and the optional arg
298 | ERR-HANDLER is nil, then return all current evalator candidates. If
299 | ERR-HANDLER is non-nil, then it is executed and its value is returned."
300 | (condition-case err
301 | (progn
302 | (if (equal "" helm-pattern)
303 | (signal 'evalator-error '("Empty Expression"))
304 | (progn (evalator-flash :success)
305 | (apply context-f args))))
306 | (error
307 | (evalator-flash :error)
308 | (if err-handler
309 | (funcall err-handler (prin1-to-string err))
310 | (evalator-history-current :candidates)))))
311 |
312 | (defun evalator-candidate-make-or-transform (&optional f-and-args err-handler)
313 | "Make initial candidates or transform current candidates.
314 | If current history index is 0 then the context's `:make-candidates'
315 | slot function and appropriate args are passed to
316 | `evalator-try-context-f' for evaluation. Otherwise the context's
317 | `:transform-candidates' slot function is used. If optional arg
318 | F-AND-ARGS is non-nil then it will be used instead. Function also
319 | accept's an optional ERR-HANDLER to pass to `evalator-try-context-f'."
320 | (with-helm-current-buffer
321 | (if f-and-args
322 | (apply 'evalator-try-context-f (append f-and-args (list err-handler)))
323 | (let* ((make-f (slot-value (evalator-state-context) :make-candidates))
324 | (transform-f (slot-value (evalator-state-context) :transform-candidates))
325 | (expr-str helm-pattern)
326 | (mode (plist-get evalator-state :mode))
327 | (f-and-args (if (equal 0 (evalator-history-index))
328 | (list make-f (list expr-str mode) err-handler)
329 | (list transform-f (list (evalator-get-candidates) expr-str nil) err-handler))))
330 | (apply 'evalator-try-context-f f-and-args)))))
331 |
332 | (defun evalator-build-source (candidates mode)
333 | "Build the source for a evalator session using a CANDIDATES list and a MODE."
334 | (helm-build-sync-source (concat "Evaluation Result" (when (equal :explicit mode) "(Explicit)"))
335 | :candidates candidates
336 | :filtered-candidate-transformer (lambda (_c _s) (evalator-candidate-make-or-transform))
337 | :help-message evalator-help-message
338 | :keymap evalator-key-map
339 | :multiline t
340 | :nohighlight t
341 | :nomark (equal :explicit mode)
342 | :persistent-help (evalator-persistent-help)
343 | :volatile t))
344 |
345 | (defun evalator-build-history-source ()
346 | "Build a source that will show the current point in history."
347 | (helm-build-dummy-source "Evalator History"
348 | :filtered-candidate-transformer (lambda (_c _s)
349 | (list (funcall evalator-config-prompt-f)))
350 | :header-line "History"
351 | :nomark t
352 | :nohighlight t))
353 |
354 | ;;;###autoload
355 | (defun evalator-insert-equiv-expr ()
356 | "Insert the equivalent expression of the previous evalator session into the current buffer."
357 | (interactive)
358 | (if (equal :explicit (plist-get evalator-state :mode))
359 | (insert (funcall
360 | (slot-value (evalator-state-context) :make-equiv-expr)
361 | (evalator-history-expression-chain)))
362 | (message "Error: This command is only allowed when the last evalator session in explicit mode.")))
363 |
364 | ;;;###autoload
365 | (defun evalator-resume ()
366 | "Resume last evalator session."
367 | (interactive)
368 | (let ((print-circle t)) ;; Necessary to support circular lists
369 | (helm-resume "*helm-evalator*")))
370 |
371 | ;;;###autoload
372 | (defun evalator (&optional mode context)
373 | "Start an evalator session.
374 |
375 | Function accepts an optional MODE keyword and a CONTEXT symbol.
376 |
377 | If MODE is non-nil and a currently supported mode value then that mode
378 | will be used for the session.
379 |
380 | Below are currently supported values for MODE:
381 |
382 | `:explicit'
383 |
384 | If MODE is nil evalator will start in normal mode.
385 |
386 | If CONTEXT is non-nil, then the result of calling CONTEXT's function
387 | definition will be used as the session's evaluation context.
388 |
389 | If CONTEXT is nil, then the current buffer's major mode will be
390 | searched for in `evalator-config-mode-context-alist'. If a match is
391 | found, the context associated with that major mode is used in the
392 | evalator session. If no match is found, an elisp evaluation context
393 | is used instead.
394 | "
395 | (interactive)
396 | (when (evalator-state-init mode (evalator-context-get context))
397 | (add-hook 'minibuffer-setup-hook (lambda ()
398 | (setq-local minibuffer-message-timeout nil) t nil))
399 | (evalator-history-push! evalator-candidates-initial "")
400 | (let* ((print-circle t) ;; Necessary to support circular lists
401 | (evalator-after-update-hook (copy-sequence helm-after-update-hook))
402 | (history-source (evalator-build-history-source))
403 | (result-source (evalator-build-source evalator-candidates-initial mode)))
404 |
405 | ;; Prevent history candidate from being selected
406 | (add-hook 'evalator-after-update-hook (lambda ()
407 | (helm-next-line)))
408 |
409 | (helm :sources (list history-source result-source)
410 | :buffer "*helm-evalator*"
411 | :prompt "Enter Expression: "
412 | :helm-after-update-hook evalator-after-update-hook))))
413 |
414 | ;;;###autoload
415 | (defun evalator-explicit (&optional context)
416 | "Helper function to start an evalator-session in explicit mode.
417 |
418 | In explicit mode the data generated will always be represented as a
419 | single candidate. This is the only mode that allows an equivalent
420 | expression of the session to be generated through
421 | `evalator-insert-equiv-expr'.
422 | "
423 | (interactive)
424 | (evalator :explicit context))
425 |
426 | (provide 'evalator)
427 |
428 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
429 | ;;; evalator.el ends here
430 |
--------------------------------------------------------------------------------
/example-gifs/practical.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seanirby/evalator/f30da4da48c0b3f3cfa1fc1c7cfdb53ffe79df36/example-gifs/practical.gif
--------------------------------------------------------------------------------
/example-gifs/walkthrough.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seanirby/evalator/f30da4da48c0b3f3cfa1fc1c7cfdb53ffe79df36/example-gifs/walkthrough.gif
--------------------------------------------------------------------------------
/test/evalator-config-test.el:
--------------------------------------------------------------------------------
1 | ;;; evalator-config-test.el ---
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 |
28 | (require 'el-mock)
29 | (eval-when-compile
30 | (require 'cl))
31 |
32 | (require 'evalator-config)
33 |
34 | ;; (ert-deftest evalator-config-prompt-f-tests ()
35 | ;; (with-mock
36 | ;; (stub evalator-history-index => 0)
37 | ;; (stub evalator-history => '(length of 3))
38 | ;; (should (equal "1 of 3"
39 | ;; (funcall evalator-config-prompt-f)))))
40 |
41 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
42 | ;;; evalator-config-test.el ends here
43 |
--------------------------------------------------------------------------------
/test/evalator-context-test.el:
--------------------------------------------------------------------------------
1 | ;;; evalator-context-test.el ---
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 | (require 'el-mock)
28 | (eval-when-compile
29 | (require 'cl))
30 |
31 | (require 'evalator-context)
32 |
33 | ;; (ert-deftest evalator-context-get-test ()
34 | ;; (let ((evalator-elisp-context "elisp")
35 | ;; (evalator-foo-context "foo")
36 | ;; (evalator-mode-context-alist '((foo-mode . evalator-foo-context))))
37 | ;; (with-mock
38 | ;; (stub buffer-local-value)
39 | ;; (should (equal "foo"
40 | ;; (evalator-context-get 'evalator-foo-context))))
41 | ;; (with-mock
42 | ;; (stub buffer-local-value => 'foo-mode)
43 | ;; (should (equal "foo"
44 | ;; (evalator-context-get))))
45 | ;; (with-mock
46 | ;; (stub buffer-local-value => 'bar-mode)
47 | ;; (should (equal "elisp"
48 | ;; (evalator-context-get))))))
49 |
50 |
51 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
52 | ;;; evalator-context-test.el ends here
53 |
--------------------------------------------------------------------------------
/test/evalator-elisp-test.el:
--------------------------------------------------------------------------------
1 | ;;; evalator-elisp-test.el --- Test for evalator-elisp-test.el
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 |
28 | (require 'ert)
29 | (require 'evalator-elisp)
30 |
31 | (defalias 'ssa 'evalator-elisp-subst-special-args)
32 | (defalias 'mc 'evalator-elisp-make-candidates)
33 | (defalias 'tc 'evalator-elisp-transform-candidates)
34 |
35 | (ert-deftest evalator-elisp-get-special-arg-tests ()
36 | (let ((evalator-context-special-arg-default "@"))
37 | (let ((evalator-elisp-special-arg "$"))
38 | (should (equal "$"
39 | (evalator-elisp-get-special-arg))))
40 | (should (equal "@"
41 | (evalator-elisp-get-special-arg)))))
42 |
43 | (ert-deftest evalator-elisp-numbered-arg-num-tests ()
44 | (should (equal 123
45 | (evalator-elisp-numbered-arg-num "@123")))
46 |
47 | (should (equal 123
48 | ;; Not expected input
49 | (evalator-elisp-numbered-arg-num "123")))
50 |
51 | (should (equal 123
52 | ;; Not expected input
53 | (evalator-elisp-numbered-arg-num "foo bar baz @123"))))
54 |
55 | (ert-deftest evalator-elisp-numbered-arg-pattern-tests ()
56 | (let ((evalator-elisp-special-arg "@"))
57 | (should (equal "@[0-9]+"
58 | (evalator-elisp-numbered-arg-pattern)))
59 | (should (equal "'?@[0-9]+"
60 | (evalator-elisp-numbered-arg-pattern t)))))
61 |
62 | (ert-deftest evalator-elisp-make-equiv-expr-tests ()
63 | (let ((evalator-elisp-special-arg "@"))
64 | (should (equal "(cons (elt (list 1 2 3) 0) (list 1 2 3))"
65 | (evalator-elisp-make-equiv-expr '("(list 1 2 3)" "(cons @0 '@)"))))))
66 |
67 | (ert-deftest evalator-elisp-subst-numbered-special-args-tests ()
68 | (let ((evalator-elisp-special-arg "@"))
69 | (should (equal "(+ @ 3 7)"
70 | (evalator-elisp-subst-numbered-special-args "(+ @ @0 @1)" '(3 7))))))
71 |
72 | (ert-deftest evalator-elisp-subst-identity-special-args-tests ()
73 | (let ((evalator-elisp-special-arg "@"))
74 | (should (equal "(+ 1 1)"
75 | (evalator-elisp-subst-identity-special-args "(+ @ @)" 1)))))
76 |
77 | (ert-deftest evalator-elisp-subst-special-args-tests ()
78 | (let ((evalator-elisp-special-arg "@"))
79 | (should (equal "'(+ 1 1)"
80 | (ssa "'(+ 1 1)" nil)))
81 |
82 | (should (equal "'(+ 1 1)"
83 | (ssa "'(+ 1 @0)" '(1 2 3))))
84 |
85 | (should (equal "'(+ 1 (+ 1 (+ 1 (+ 1 (+ 1 1)))))"
86 | (ssa "'(+ 1 (+ 1 (+ 1 (+ 1 (+ 1 @0)))))" [1 2 3])))
87 |
88 | (should (equal "1"
89 | (ssa "@" 1)))))
90 |
91 | (ert-deftest evalator-elisp-make-candidates-tests ()
92 | ;;normal mode
93 | (should (equal '("1" "2" "3")
94 | (mc "'(1 2 3)" :normal)))
95 |
96 | (should (equal '("1" "2" "3")
97 | (mc "[1 2 3]" :normal)))
98 |
99 | (should (equal '("\"foo\"" "\"bar\"" "\"baz\"")
100 | (mc "'(\"foo\" \"bar\" \"baz\")" :normal)))
101 |
102 | (should (equal '("\"foo\"" "\"bar\"" "\"baz\"")
103 | (mc "[\"foo\" \"bar\" \"baz\"]" :normal)))
104 |
105 | (should (equal '("\"foo\"")
106 | (mc "\"foo\"" :normal)))
107 |
108 | (should (equal '(":foo")
109 | (mc ":foo" :normal)))
110 |
111 | ;;explicit mode
112 | (should (equal '("(1 2 3)")
113 | (mc "'(1 2 3)" :explicit)))
114 |
115 | (should (equal '("[1 2 3]")
116 | (mc "[1 2 3]" :explicit)))
117 |
118 | (should (equal '("(\"foo\" \"bar\" \"baz\")")
119 | (mc "'(\"foo\" \"bar\" \"baz\")" :explicit)))
120 |
121 | (should (equal '("[\"foo\" \"bar\" \"baz\"]")
122 | (mc "[\"foo\" \"bar\" \"baz\"]" :explicit)))
123 |
124 | (should (equal '("\"foo\"")
125 | (mc "\"foo\"" :explicit)))
126 |
127 | (should (equal '(":foo")
128 | (mc ":foo" :explicit))))
129 |
130 | (ert-deftest evalator-elisp-transform-candidates-tests ()
131 | (let ((evalator-elisp-special-arg "@"))
132 | (should (equal '("1" "2" "3" "4")
133 | (tc '("0" "1" "2" "3") "(+ 1 @)" nil)))
134 |
135 | (should (equal '("\"foo1\"" "\"bar1\"" "\"baz1\"")
136 | (tc '("\"foo\"" "\"bar\"" "\"baz\"") "(concat @ \"1\")" nil)))
137 |
138 | (should (equal '("4")
139 | (tc '("1" "3") "(cl-reduce '+ '@)" t)))
140 |
141 | (should (equal '("\"foobar\"")
142 | (tc '("\"foo\"" "\"bar\"") "(concat @0 @1)" t)))))
143 |
144 | (ert-deftest evalator-elisp-eval-tests ()
145 | (should (equal 2
146 | (evalator-elisp-eval "(+ 1 1)"))))
147 |
148 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
149 | ;;; evalator-elisp-test.el ends here
150 |
--------------------------------------------------------------------------------
/test/evalator-history-test.el:
--------------------------------------------------------------------------------
1 | ;;; evalator-history-test.el --- Tests for evalator-history-test.el
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 |
28 | (require 'evalator-history)
29 | (require 'cl-lib)
30 | (require 'noflet)
31 |
32 | (ert-deftest evalator-history-test ()
33 | "Test that 'evalator-history' returns the ':history' value of
34 | 'evalator-state'"
35 | (let ((state-copy (copy-sequence evalator-state)))
36 | (unwind-protect
37 | (progn (setq evalator-state '(:history :foo))
38 | (should (equal :foo (evalator-history))))
39 | (setq evalator-state state-copy))))
40 |
41 | (ert-deftest evalator-history-index-test ()
42 | "Tests that 'evalator-history-index' returns the ':history-index'
43 | property of 'evalator-state'"
44 | (let ((evalator-state '(:history-index 0)))
45 | (should (equal 0 (evalator-history-index)))))
46 |
47 | (ert-deftest evalator-history-push!-test ()
48 | "Tests that 'evalator-history-push!' can push onto state's history
49 | and update state's index."
50 | (let ((evalator-state '(:history [] :history-index -1)))
51 | (progn
52 | (evalator-history-push! '("foo") "bar")
53 | (should (equal
54 | '(:history [(:candidates ("foo") :expression "bar")] :history-index 0)
55 | evalator-state)))))
56 |
57 | (ert-deftest evalator-history-current-test ()
58 | (noflet ((evalator-history () ["foo" "bar" "baz"])
59 | (evalator-history-index () 1))
60 | (should (equal "bar"
61 | (evalator-history-current)))))
62 |
63 | (ert-deftest evalator-history-expression-chain-tests ()
64 | "Tests the 'evalator-history-expression-chain' returns all
65 | expressions in history except for the first"
66 | (let ((evalator-state '(:history [(:expression nil)
67 | (:expression "(expr1)")
68 | (:expression "(expr2)")
69 | (:expression "(expr3)")])))
70 | (should (equal '("(expr1)" "(expr2)" "(expr3)")
71 | (evalator-history-expression-chain)))))
72 |
73 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
74 | ;;; evalator-history-test.el ends here
75 |
--------------------------------------------------------------------------------
/test/evalator-state-test.el:
--------------------------------------------------------------------------------
1 | ;;; evalator-state-test.el --- Tests for evalator-state.el
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 |
28 | (require 'ert)
29 | (require 'el-mock)
30 | (require 'evalator-state)
31 |
32 | (ert-deftest evalator-state-init-test ()
33 | "Tests that 'evalator state' is initialized with the value of
34 | 'evalator-state-default'"
35 | (with-mock
36 | (stub evalator-elisp-context)
37 | (mock (evalator-utils-put! * * *))
38 | (stub evalator-context-get)
39 | (mock (slot-value * :init) => (lambda () t))
40 | (stub evalator-state-context)
41 | (let ((evalator-foo-context t)
42 | (evalator-state-default (copy-sequence evalator-state-default)))
43 | (with-mock
44 | (should (evalator-state-init)))
45 | (with-mock
46 | (should (evalator-state-init :explicit)))
47 | (with-mock
48 | (should (evalator-state-init :explicit evalator-foo-context))))))
49 |
50 | (ert-deftest evalator-state-context-test ()
51 | (let* ((evalator-state (list :context 'evalator-foo-context)))
52 | (with-mock
53 | (stub evalator-foo-context => (lambda () t))
54 | (should (evalator-state-context)))))
55 |
56 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
57 | ;;; evalator-state-test.el ends here
58 |
--------------------------------------------------------------------------------
/test/evalator-test.el:
--------------------------------------------------------------------------------
1 | ;;; evalator-test.el --- Tests for evalator.el
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 |
28 | (require 'noflet)
29 | (require 'el-mock)
30 | (eval-when-compile
31 | (require 'cl))
32 | (require 'evalator)
33 |
34 | (ert-deftest evalator-action-previous-and-next-tests ()
35 | (let ((evalator-state (list :history [(:candidates '())
36 | (:candidates '())]
37 | :history-index 0)))
38 | (noflet ((helm-unmark-all () nil)
39 | (helm-set-pattern (_) nil)
40 | (helm-update () nil))
41 |
42 | (evalator-action-previous)
43 | (should (equal 0 (evalator-history-index)))
44 |
45 | (evalator-action-next)
46 | (should (equal 1 (evalator-history-index)))
47 |
48 | (evalator-action-next)
49 | (should (equal 1 (evalator-history-index)))
50 |
51 | (evalator-action-previous)
52 | (should (equal 0 (evalator-history-index))))))
53 |
54 | (ert-deftest evalator-action-execute-in-elisp-test ()
55 | (let ((helm-pattern "@ !"))
56 | (noflet ((evalator-context-get-special-arg (arg) arg))
57 | (with-mock
58 | (stub evalator-elisp-context => "!")
59 | (stub evalator-state-context => "@")
60 | (stub evalator-get-candidates => '("cand-1"))
61 | (mock (evalator-elisp-transform-candidates '("cand-1") "! !" nil) => '("cand-1-xfrmd"))
62 | (mock (message "(\"cand-1-xfrmd\")") => t)
63 | (evalator-action-execute-in-elisp))
64 | (with-mock
65 | (stub evalator-elisp-context => "@")
66 | (stub evalator-state-context => "@")
67 | (stub evalator-get-candidates => '("cand-1"))
68 | (mock (evalator-elisp-transform-candidates '("cand-1") "@ !" nil) => '("cand-1-xfrmd"))
69 | (mock (message "(\"cand-1-xfrmd\")") => t)
70 | (evalator-action-execute-in-elisp))
71 | (with-mock
72 | (stub evalator-elisp-context)
73 | (stub evalator-state-context)
74 | (stub evalator-get-candidates)
75 | (stub evalator-elisp-transform-candidates => (signal 'evalator-error '("error")))
76 | (stub evalator-message => nil)
77 | (stub error => t)
78 | (evalator-action-execute-in-elisp)) )))
79 |
80 | (ert-deftest evalator-action-confirm-make-or-transform-test ()
81 | ;;Successful transformation
82 | (with-mock
83 | (mock (evalator-candidate-make-or-transform * *) => t)
84 | (mock (evalator-history-push! * *) :times 1)
85 | (mock (helm-unmark-all) :times 1)
86 | (mock (helm-set-pattern "") :times 1 => t)
87 | (evalator-action-confirm-make-or-transform))
88 | ;;Unsuccessful transformation
89 | (with-mock
90 | (mock (evalator-candidate-make-or-transform * *) => nil)
91 | (should (equal nil
92 | (evalator-action-confirm-make-or-transform)))))
93 |
94 | (ert-deftest evalator-action-confirm-transform-collect-test ()
95 | (let ((helm-pattern "pattern"))
96 | (with-mock
97 | (stub slot-value => "f")
98 | (stub evalator-get-candidates => '("cand-1"))
99 | (mock (evalator-action-confirm-make-or-transform '("f" (("cand-1") "pattern" t))))
100 | (evalator-action-confirm-transform-collect))))
101 |
102 | (ert-deftest evalator-action-insert-special-arg-test ()
103 | (with-mock
104 | (stub evalator-state-context)
105 | (stub evalator-context-get-special-arg => "@")
106 | (should (equal "@"
107 | (with-temp-buffer
108 | (evalator-action-insert-special-arg)
109 | (buffer-string))))))
110 |
111 | (ert-deftest evalator-flash-test ()
112 | (with-mock
113 | (mock (face-remap-add-relative * 'evalator-success) => t)
114 | (evalator-flash :success))
115 | (with-mock
116 | (mock (face-remap-add-relative * 'evalator-error) => t)
117 | (evalator-flash :error)))
118 |
119 | ;; TODO
120 | (ert-deftest evalator-unmark-all-test ())
121 |
122 | ;; TODO
123 | (ert-deftest evalator-marked-candidates-test ())
124 |
125 | (ert-deftest evalator-persistent-help-test ()
126 | (let ((evalator-key-map (list 'evalator-action-previous "C-l"
127 | 'evalator-action-confirm-make-or-transform "RET"
128 | 'evalator-action-insert-special-arg "C-c ;")))
129 | (noflet ((where-is-internal (command key-map _) (plist-get key-map command))
130 | (key-description (str) str))
131 | (should (equal (concat "History forward, "
132 | "C-l: History backward, "
133 | "RET: Accept transformation, "
134 | "C-c ;: Insert special arg")
135 | (evalator-persistent-help))))))
136 |
137 | (ert-deftest evalator-get-candidates-test ()
138 | (with-mock
139 | (mock (evalator-history-current :candidates) => '("cand-1" "cand-2"))
140 | (mock (evalator-marked-candidates) => nil)
141 | (should (equal '("cand-1" "cand-2")
142 | (evalator-get-candidates))))
143 | (with-mock
144 | (mock (evalator-history-current :candidates) => '("cand-1" "cand-2"))
145 | (mock (evalator-marked-candidates) => '("cand-2"))
146 | (should (equal '("cand-2")
147 | (evalator-get-candidates)))))
148 |
149 | (ert-deftest evalator-try-context-f-test ()
150 | ;; successful call
151 | (let ((helm-pattern "(non-empty expression)")
152 | (context-f (lambda (&rest args) t)))
153 | (with-mock
154 | (mock (evalator-flash :success) :times 1)
155 | (evalator-try-context-f context-f nil nil)))
156 | ;; trigger error because of empty pattern
157 | (let ((helm-pattern "")
158 | (context-f (lambda (&rest args) t)))
159 | (with-mock
160 | (mock (evalator-flash :error) :times 1)
161 | (mock (evalator-history-current :candidates) :times 1 => t)
162 | (evalator-try-context-f context-f nil nil)))
163 | ;; error handler gets called
164 | (let ((helm-pattern "(non-empty expression)")
165 | (context-f (lambda (&rest args) (signal 'evalator-error '(""))))
166 | (err-handler (lambda (_) t)))
167 | (with-mock
168 | ;; Flashes :success then :error
169 | (mock (evalator-flash *) :times 2)
170 | (evalator-try-context-f context-f nil err-handler))))
171 |
172 | (ert-deftest evalator-make-or-transform-test ()
173 | (let ((helm-pattern "pattern"))
174 | (with-mock
175 | (stub slot-value)
176 | (mock (evalator-try-context-f "f" "args" "err-handler") => t)
177 | (evalator-candidate-make-or-transform '("f" "args") "err-handler"))
178 | (with-mock
179 | (stub slot-value)
180 | (stub evalator-history-index => 0)
181 | (mock (evalator-try-context-f * '("pattern" :normal) *))
182 | (evalator-candidate-make-or-transform))
183 | (with-mock
184 | (stub slot-value)
185 | (stub evalator-history-index => 1)
186 | (stub evalator-get-candidates => '("cand-1"))
187 | (mock (evalator-try-context-f * '(("cand-1") "pattern" nil) *))
188 | (evalator-candidate-make-or-transform))))
189 |
190 | ;; Tried to mock the helm-build-sync-source macro but ran into issues
191 | ;; This works for now...
192 | (ert-deftest evalator-build-source-test ()
193 | (let ((args-normal (evalator-build-source nil :normal))
194 | (args-explicit (evalator-build-source nil :explicit)))
195 | (should (equal "Evaluation Result"
196 | (cdr (assoc 'name args-normal))))
197 | (should (equal "Evaluation Result(Explicit)"
198 | (cdr (assoc 'name args-explicit))))))
199 |
200 | (ert-deftest evalator-build-history-source-test ()
201 | (with-mock
202 | (stub helm-build-dummy-source => t)
203 | (evalator-build-history-source)))
204 |
205 | (ert-deftest evalator-insert-equiv-expr-test ()
206 | (with-mock
207 | (stub slot-value => (lambda (exprs) (car exprs)))
208 | (stub evalator-history-expression-chain => '("(+ 1 1)"))
209 | (stub message => "Error message output")
210 | (stub evalator-state-context)
211 | (let ((evalator-state (list :mode :explicit)))
212 | (with-temp-buffer
213 | (evalator-insert-equiv-expr)
214 | (should (equal "(+ 1 1)"
215 | (buffer-string)))))
216 | (let ((evalator-state (list :mode nil)))
217 | (should (equal "Error message output"
218 | (evalator-insert-equiv-expr))))))
219 |
220 | (ert-deftest evalator-resume-test ()
221 | (with-mock
222 | (mock (helm-resume "*helm-evalator*"))
223 | (evalator-resume)))
224 |
225 | (ert-deftest evalator-test ()
226 | (let ((evalator-foo-context "foo")
227 | (state-init-p nil)
228 | (history nil)
229 | (evalator-candidates-initial '("foo")))
230 | (with-mock
231 | (mock (evalator-context-get 'evalator-foo-context) => evalator-foo-context)
232 | (mock (evalator-state-init :explicit "foo") => t)
233 | (mock (evalator-history-push! * *) :times 1)
234 | (stub evalator-build-history-source => t)
235 | (mock (evalator-build-source '("foo") :explicit) => t)
236 | (mock (helm :sources '(t t)
237 | :buffer "*helm-evalator*"
238 | :prompt "Enter Expression: "
239 | :helm-after-update-hook *) => t)
240 | (should (evalator :explicit 'evalator-foo-context)))))
241 |
242 | (ert-deftest evalator-explicit-test ()
243 | (with-mock
244 | (mock (evalator :explicit *) => t)
245 | (evalator-explicit)))
246 |
247 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
248 | ;;; evalator-test.el ends here
249 |
--------------------------------------------------------------------------------
/test/evalator-utils-test.el:
--------------------------------------------------------------------------------
1 | ;;; evalator-utils-test.el --- Tests for evalator-utils.el
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 |
28 | (require 'evalator-utils)
29 | (require 'noflet)
30 |
31 | (ert-deftest evalator-utils-put!-test ()
32 | (let ((plst '(:foo :bar)))
33 | (evalator-utils-put! plst :foo :baz)
34 | (should (equal '(:foo :baz) plst))))
35 |
36 | (ert-deftest evalator-utils-get-file-string ()
37 | (noflet ((insert-file-contents (filepath) (insert "foo")))
38 | (should (equal "foo" (evalator-utils-get-file-string "/dummy/file/path")))))
39 |
40 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
41 | ;;; evalator-utils-test.el ends here
42 |
--------------------------------------------------------------------------------
/test/test-helper.el:
--------------------------------------------------------------------------------
1 | ;;; test-helper.el --- Test helpers
2 | ;;
3 | ;; Author: Sean Irby
4 | ;; Copyright © , Sean Irby
5 | ;;
6 | ;; This program is free software: you can redistribute it and/or modify
7 | ;; it under the terms of the GNU General Public License as published by
8 | ;; the Free Software Foundation, either version 3 of the License, or (at
9 | ;; your option) any later version.
10 | ;;
11 | ;; This program is distributed in the hope that it will be useful, but
12 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ;; General Public License for more details.
15 | ;;
16 | ;; You should have received a copy of the GNU General Public License
17 | ;; along with GNU Emacs. If not, see .
18 | ;;
19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 | ;;
21 | ;;; This file is not a part of GNU Emacs
22 | ;;
23 | ;;; Commentary:
24 | ;;
25 | ;;; Code:
26 |
27 |
28 |
29 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
30 | ;;; test-helper.el ends here
31 |
--------------------------------------------------------------------------------