reddit / emacs
Which Emacs package is equivalent to vim-crunch?
LowCom · 2022-05-27 11:53
This is the functionality I need which vim crunch does.
Let's say there is this line:.
34 + 5.
Typing g== on this line will append "=39" to the expression.
And then there's this paragraph.
X = 6
Y = 7.
X * Y
Typing g=ip on this paragraph appends "=42" to the last line.
Which package does this in Emacs?
RavenLance · 2022-05-27 12:57
Emacs comes with a built-in package called '*calc*' which is a very functional calculator with a variety of ways to approach using it.
I would highly recommend looking through the calc manuals introduction items, as it may not be as intuitive being a stack based calculator natively. It can handle standard algebraic formulas as input as well, but yhat input needs to be specified.
I believe there is an "embedded mode" that can be used inside a buffer directly if you need that functionality specifically. I don't know if it compares to vim-crunch based on the above examples though.
FLMKane · 2022-05-27 13:05
Emacs lisp can do that but it'll be more verbose and you'll have to delete your code afterwards
Type 35 + 4 = (+ 35 4) , then press C-U C-X C-E
You get the following line
35 + 4 = (+ 35 4) 39
Delete the lisp code and you get
35 + 4 = 39
Tohiko · 2022-05-27 13:48
I have the following code:
```
(defun calc-eval-region (beg end)
"Calculate the region and display the result in the echo area.
If i is pressed after, insert the result at the end of region.
If r is pressed replace the text with the result"
(interactive (if (use-region-p)
(list (region-beginning) (region-end))
(list (point-at-bol) (point-at-eol))))
(let* ((expr (buffer-substring-no-properties beg end))
(result (calc-eval expr))
(my-beg beg)
(my-end end) map)
(message "%s = %s" expr result)
(when (not (listp result))
(setq map (make-sparse-keymap))
(define-key map "i"
(lambda () (interactive) (goto-char my-end) (insert " = " result)))
(define-key map "r"
(lambda ()
(interactive)
(kill-region my-beg my-end)
(goto-char my-beg)
(insert result)))
(set-transient-map map))))
```
If the function called when a region is selected (or an expression is on a line), the calculation is made and is inserted by pressing `i`. If `r` is pressed the expression is replaced with the result instead. I haven't checked how to make it work with multi-line statements, but it should be possible.
bravosierrasierra · 2022-05-27 13:51
my solution, mapped on "C-x ="
```
;; OMG!! (calc-eval "390.0/100*15") == 0.26 instead 58.5 !!!
(setq calc-multiplication-has-precedence nil)
;; https://www.reddit.com/r/emacs/comments/445w6s/whats_some_small_thing_in_your_dotemacs_that_you/
(defun my-calc-eval-region (arg)
"Evaluate an expression in calc and communicate the result.
If the region is active evaluate that, otherwise search backwards to the first whitespace character
to find the beginning of the expression. By default, keep the expression and insert the result into
the buffer after it. If called with the universal prefix argument, replace the expression with its
value. If called with a negative prefix argument, just echo the result in the minibuffer.
"
(interactive "p")
(let (start end)
(if (use-region-p)
(setq start (region-beginning) end (region-end))
(progn
(setq end (point))
(setq start (search-backward-regexp "\\s-\\|\n" 0 1))
(setq start (1+ (if start start 0)))
(goto-char end)))
(let ((value (calc-eval (buffer-substring-no-properties start end))))
(message (format "Evaluate value: %s" (buffer-substring-no-properties start end)))
(pcase arg
(4 (delete-region start end))
(1 (insert " = ")))
(pcase arg
((or 1 4) (insert value))
(-1 (message value))))))
(global-set-key (kbd "C-x =") 'my-calc-eval-region)
```
edumerco · 2022-05-27 18:34
For small, on the fly calculations, try quick-calc :)
wakatara · 2022-05-27 18:37
I'd also throw in \`literate-calc-mode\` package here which provides a Calca-like experience for writing math and having it calculate on the fly. Looking at vim-crunch it seems similar (but resuls get calced automatically) so I think of it more as a symbolic math calculator in text.
It is built on top of calc afaik but I think you'll find it a lot more what you're looking for and avoids having to learn the ins and outs of calc or lisp.
Kaligule · 2022-05-27 19:41
Cool. The second part doesn't seem to work for me, though. I get the message, but then a "Symbol’s value as variable is void: my-end" and no option for `i` an `r`. Can someone confirm this?
Kribbstar · 2022-05-27 20:10
It require's lexical binding, otherwise the closure (the lambda) won't retain the variable scope.
ideasman_42 · 2022-05-28 13:04
I use this to replace the selection with it's evaluated Python result (works for lots of maths expressions).
```
(defun shell-command-on-region-and-select
(start
end
command
&optional
output-buffer
replace
error-buffer
display-error-buffer
region-noncontiguous-p)
"Wrapper for 'shell-command-on-region', re-selecting the output.
Useful when called with a selection, so it can be modified in-place."
(interactive)
(let ((buffer-size-init (buffer-size)))
(shell-command-on-region
start
end
command
output-buffer
replace
error-buffer
display-error-buffer
region-noncontiguous-p)
(setq deactivate-mark nil)
(setq end (+ end (- (buffer-size) buffer-size-init)))
(set-mark start)
(goto-char end)
(activate-mark)
;; needed for evil line mode
(when (and (boundp evil-state) (string= evil-state "visual"))
(when (eq (evil-visual-type) evil-visual-line)
(evil-visual-select start end 'line)))))
;; Also handy sometimes.
(defun eval-region-as-lisp ()
(interactive)
(let ((value (eval (elisp--preceding-sexp))))
(kill-sexp -1)
(insert (format "%S" value))))
(defun eval-region-as-py ()
"Evaluate selection as a python expression, replacing it with the result."
(interactive)
(shell-command-on-region-and-select (region-beginning) (region-end)
"python -c 'import sys; sys.stdout.write(str((eval(sys.stdin.read()))))'"
0
t))
```
Tohiko · 2022-05-28 17:57
As @Kribbstar said, make share that `lexical-binding` is set to `t` before defining the function.
Kaligule · 2022-05-28 19:40
I am a bit unsure whether I want to do that. I have no strong feelings about lexical bindings one way or another. But should I really change the way variables work in my whole config, just to make this function work? With the chance that it breaks something I wouldn't notice immediately? It feels bad to change a global variable with potential effects on everything, just for a small feature in calc.
Starlight100 · 2022-05-29 16:20
> Emacs lisp can do that but it'll be more verbose
If there are more than 2 operands the lisp notation will be more concise.
(+ 4 4 4 4 4 4 4 4)
vs
4 + 4 + 4 + 4 + 4 + 4 + 4 + 4
Tohiko · 2022-05-30 08:17
You don't need to change it in your whole config. The typical way is to put the functions that need lexical binding in a separate file and enable lexical binding locally (using a local variable). Most packages that you load probably do something similar (look near the top for `lexical-bind:t` comment).
Kaligule · 2022-05-30 08:39
I figured that I could do that, but then I would need multiple config files....
I went the other way and rewrote your function to not needing lexical binding.
I am just learning to read/write emacs-lisp, so that was a cool excercise.
Thank you for your brilliant ideas, I like it a lot.
```lisp
(defun calc-eval-region (beg end)
"Calculate the region and insert it into the current buffer."
(interactive (if (use-region-p)
(list (region-beginning) (region-end))
(list (point-at-bol) (point-at-eol))))
(let* ((expr (buffer-substring-no-properties beg end))
(result (calc-eval expr))
(my-beg beg)
(my-end end) map)
(if (not (listp result))
(insert-result beg end result))))
(defun insert-result (beg end result)
"Insert the result into the current buffer.
There are two ways to do that:
Either by replacing the term (which is defined by beg-end)
or by inserting after it."
(let ((choice (nth 1 (read-multiple-choice
(format "What should be done with the result (%s)?" result)
'((?i "insert" "insert after the term")
(?r "replace" "replace the term"))))))
(pcase choice
("insert" (progn
(goto-char end)
(insert " = " result)))
("replace" (progn
(kill-region beg end)
(goto-char beg)
(insert result))))))
```
LowCom · 2022-06-02 09:32
The code is not readable even thought you put it in backticks for some reason.
Can you please format it correctly?
LowCom · 2022-06-03 05:59
Just tried this and is exactly what I am looking for, much better than even vim crunch. Thank you.
wakatara · 2022-06-10 08:44
Glad it helped you out. Enjoy!