Permanently change the state of abbreviated display of a piece of code? - emacs

In *scratch* buffer in Emacs Lisp after you evaluated an expression that evaluates to a complex Lisp form, that form is "abbreviated", i.e. some long lists or its inner parts are replaced by ellipses. Looks something like:
(let* ((--3 (make-hash-table)) d c (--5 (let ... ... ...)) (--6 0) (--0 (make-
hash-table)) b a (--1 0) --7) (catch (quote --2) (maphash (lambda ... ... ...
... ... ... ...) --0)) (nreverse --7))
vs expanded version:
(let* ((--3 (make-hash-table)) d c (--5 (let (--4) (maphash (lambda (k v)
(setq --4(cons k --4))) --3) (nreverse --4))) (--6 0) (--0 (make-hash-table))
b a (--1 0) --7) (catch (quote --2) (maphash (lambda (k v) (when (or (> --6
150) (> --1 100)) (throw (quote --2) nil)) (setq a k b v) (setq c (car --5) d
(gethash (car --5) --3) --5 (cdr --5)) (incf --6) (setq --7 (cons (list (cons
a b) (cons c d)) --7)) (message "a: %s, b: %s, c: %s, d: %s" a b c d)) --0))
(nreverse --7))
If I press RET in the expanded or collapsed state, it toggles the state back. Obviously, my first reaction is to try to format the output, so I press RET! And then it will either collapse or expand, depends on whichever state it was in. If I copy and paste the whole thing, then it is treated as normal text, but is there a faster way of doing it? I.e. I would like to permanently expand it, w/o having to copy-paste.
I couldn't find the function which toggles the state (perhaps I'm calling it incorrectly). It took me a while to realize it was possible to toggle it anyway (yeah, it displays that in a tooltip, but who uses mouse in Emacs?).
Also, I, in general, like the idea, is it possible to apply it to other languages too? Where can I read more about this feature?

Two variables control the print out of results of eval-expression. in the *scratch* buffer:
eval-expression-print-length
eval-expression-print-level
You could set those to nil and the result would always be expanded.
If you just want the RET to switch to the fully expanded (and not to toggle), you can use this advice to strip the text properties which enable the toggling of display state:
(defadvice last-sexp-toggle-display (after last-sexp-toggle-display-only-long-form activate)
"After the function is called, check to see if the long form had been displayed, and if so, remove property enabling toggling"
(save-restriction
(widen)
(let ((value (get-text-property (point) 'printed-value)))
(when value
(let ((beg (or (previous-single-property-change (min (point-max) (1+ (point)))
'printed-value)
(point)))
(end (or (next-single-char-property-change (point) 'printed-value) (point)))
(standard-output (current-buffer))
(point (point)))
(if (< (length (nth 1 value)) (length (nth 2 value)))
(remove-text-properties beg end '(printed-value))))))))

Related

Destructuring bind for regex matches

In elisp, how can I get a destructuring bind for regex matches?
For example,
;; what is the equivalent of this with destructuring?
(with-temp-buffer
(save-excursion (insert "a b"))
(re-search-forward "\\(a\\) \\(b\\)")
(cons (match-string 1)
(match-string 2)))
;; trying to do something like the following
(with-temp-buffer
(save-excursion (insert "a b"))
(cl-destructuring-bind (a b) (re-search-forward "\\(a\\) \\(b\\)")
(cons a b)))
I was thinking I would have to write a macro to expand matches if there isn't another way.
Here is one way: you first extend pcase to accept a new re-match pattern, with a definition such as:
(pcase-defmacro re-match (re)
"Matches a string if that string matches RE.
RE should be a regular expression (a string).
It can use the special syntax \\(?VAR: to bind a sub-match
to variable VAR. All other subgroups will be treated as shy.
Multiple uses of this macro in a single `pcase' are not optimized
together, so don't expect lex-like performance. But in order for
such optimization to be possible in some distant future, back-references
are not supported."
(let ((start 0)
(last 0)
(new-re '())
(vars '())
(gn 0))
(while (string-match "\\\\(\\(?:\\?\\([-[:alnum:]]*\\):\\)?" re start)
(setq start (match-end 0))
(let ((beg (match-beginning 0))
(name (match-string 1 re)))
;; Skip false positives, either backslash-escaped or within [...].
(when (subregexp-context-p re start last)
(cond
((null name)
(push (concat (substring re last beg) "\\(?:") new-re))
((string-match "\\`[0-9]" name)
(error "Variable can't start with a digit: %S" name))
(t
(let* ((var (intern name))
(id (cdr (assq var vars))))
(unless id
(setq gn (1+ gn))
(setq id gn)
(push (cons var gn) vars))
(push (concat (substring re last beg) (format "\\(?%d:" id))
new-re))))
(setq last start))))
(push (substring re last) new-re)
(setq new-re (mapconcat #'identity (nreverse new-re) ""))
`(and (pred stringp)
(app (lambda (s)
(save-match-data
(when (string-match ,new-re s)
(vector ,#(mapcar (lambda (x) `(match-string ,(cdr x) s))
vars)))))
(,'\` [,#(mapcar (lambda (x) (list '\, (car x))) vars)])))))
and once that is done, you can use it as follows:
(pcase X
((re-match "\\(?var:[[:alpha:]]*\\)=\\(?val:.*\\)")
(cons var val)))
or
(pcase-let
(((re-match "\\(?var:[[:alpha:]]*\\)=\\(?val:.*\\)") X))
(cons var val))
This has not been heavily tested, and as mentioned in the docstring it doesn't work as efficiently as it (c|sh)ould when matching a string against various regexps at the same time. Also you only get the matched substrings, not their position. And finally, it applies the regexp search to a string, whereas in manny/most cases regexps searches are used in a buffer. But you may still find it useful.

Customize Elisp plist indentation

I don't like how plists are indented in Elisp.
;; current desired Python (for comparison)
;; '(a 1 '(a 1 {'a': 1,
;; b 2 b 2 'b': 2,
;; c 3) c 3) 'c': 3}
Tried on M-x emacs-version 24.3.1, ran emacs -Q, typed the plist and pressed C-x h C-M-\.
This indentation makes sense when it isn't a list:
(mapcar (lambda (x) (x + 1))
'(1 2 3 4))
How do I change formatting settings so that only plists (or, if that's impossible, all quoted lists) have the desired rectangular indentation, but indentation of everything else stays the same? I need this stored locally in an .el file, so that when I edit this file, it is indented as desired, but this behavior doesn't end up anywhere else.
Found it:
(setq lisp-indent-function 'common-lisp-indent-function)
Here's a sample file:
(setq x '(a 1
b 2
c 3))
;;; Local Variables:
;;; lisp-indent-function: common-lisp-indent-function
;;; End:
I'll just dump my whole indentation config here:
(setq lisp-indent-function 'common-lisp-indent-function)
(put 'cl-flet 'common-lisp-indent-function
(get 'flet 'common-lisp-indent-function))
(put 'cl-labels 'common-lisp-indent-function
(get 'labels 'common-lisp-indent-function))
(put 'if 'common-lisp-indent-function 2)
(put 'dotimes-protect 'common-lisp-indent-function
(get 'when 'common-lisp-indent-function))
You can fix this (in my opinion) bug by overriding lisp-indent-function. The original source of the hack was this Github Gist, which was referenced with some more explanation from this Emacs Stack Exchange answer.
However, I was very uncomfortable overriding a core function like this. For one, it's very opaque—how is a reader supposed to tell what is changed? And worse—what if the official definition of lisp-indent-function changed in the future? How would I know that I needed to update my hack?
As a response, I created the library el-patch, which is specifically designed to address this problem. After installing the package, you can override lisp-indent-function as follows:
(el-patch-defun lisp-indent-function (indent-point state)
"This function is the normal value of the variable `lisp-indent-function'.
The function `calculate-lisp-indent' calls this to determine
if the arguments of a Lisp function call should be indented specially.
INDENT-POINT is the position at which the line being indented begins.
Point is located at the point to indent under (for default indentation);
STATE is the `parse-partial-sexp' state for that position.
If the current line is in a call to a Lisp function that has a non-nil
property `lisp-indent-function' (or the deprecated `lisp-indent-hook'),
it specifies how to indent. The property value can be:
* `defun', meaning indent `defun'-style
(this is also the case if there is no property and the function
has a name that begins with \"def\", and three or more arguments);
* an integer N, meaning indent the first N arguments specially
(like ordinary function arguments), and then indent any further
arguments like a body;
* a function to call that returns the indentation (or nil).
`lisp-indent-function' calls this function with the same two arguments
that it itself received.
This function returns either the indentation to use, or nil if the
Lisp function does not specify a special indentation."
(el-patch-let (($cond (and (elt state 2)
(el-patch-wrap 1 1
(or (not (looking-at "\\sw\\|\\s_"))
(looking-at ":")))))
($then (progn
(if (not (> (save-excursion (forward-line 1) (point))
calculate-lisp-indent-last-sexp))
(progn (goto-char calculate-lisp-indent-last-sexp)
(beginning-of-line)
(parse-partial-sexp (point)
calculate-lisp-indent-last-sexp 0 t)))
;; Indent under the list or under the first sexp on the same
;; line as calculate-lisp-indent-last-sexp. Note that first
;; thing on that line has to be complete sexp since we are
;; inside the innermost containing sexp.
(backward-prefix-chars)
(current-column)))
($else (let ((function (buffer-substring (point)
(progn (forward-sexp 1) (point))))
method)
(setq method (or (function-get (intern-soft function)
'lisp-indent-function)
(get (intern-soft function) 'lisp-indent-hook)))
(cond ((or (eq method 'defun)
(and (null method)
(> (length function) 3)
(string-match "\\`def" function)))
(lisp-indent-defform state indent-point))
((integerp method)
(lisp-indent-specform method state
indent-point normal-indent))
(method
(funcall method indent-point state))))))
(let ((normal-indent (current-column))
(el-patch-add
(orig-point (point))))
(goto-char (1+ (elt state 1)))
(parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)
(el-patch-swap
(if $cond
;; car of form doesn't seem to be a symbol
$then
$else)
(cond
;; car of form doesn't seem to be a symbol, or is a keyword
($cond $then)
((and (save-excursion
(goto-char indent-point)
(skip-syntax-forward " ")
(not (looking-at ":")))
(save-excursion
(goto-char orig-point)
(looking-at ":")))
(save-excursion
(goto-char (+ 2 (elt state 1)))
(current-column)))
(t $else))))))
Here is another less heavyweight solution, based on emacsql-fix-vector-indentation. An advice around calculate-lisp-indent is sufficient.
This only works for plists that use keywords as keys, but that covers a majority of plists. To make this work on quoted lists instead, you could change the looking-at regexp to detect the ' or "`", but that will not cover, say, a nested list.
This can further be packaged up into a minor mode if there is a need to turn it off.
(defun my/inside-plist? ()
"Is point situated inside a plist?
We determine a plist to be a list that starts with a keyword."
(let ((start (point)))
(save-excursion
(beginning-of-defun)
(let ((sexp (nth 1 (parse-partial-sexp (point) start))))
(when sexp
(setf (point) sexp)
(looking-at (rx "(" (* (syntax whitespace)) ":")))))))
(define-advice calculate-lisp-indent (:around (func &rest args)
plist)
(if (save-excursion
(beginning-of-line)
(my/inside-plist?))
(let ((lisp-indent-offset 1))
(apply func args))
(apply func args)))

Subsequently running the same function, yielding different results

I feel a bit silly for asking this question, but I feel like my code is as inefficient as it can be. I think I do not have the logic going on too well here.
Basically, I would like to have some different things happen on subsequently running the same commands.
My idea was to have a (cond ), in which for each case I have a test whether the command used before is the same AND the value of a variable which is set according to how many times it was pressed.
I also feel like I am not getting the title/tags correctly in this case, so feel free to edit.
((and (eq last-repeatable-command 'thecommand)
(= varcounter 1))
(message "second time called")
(setq varcounter 2))
When it is pressed again, the next clause would fire.
While the code below works, I believe this could be done way more efficiently, and I hope someone can give directions on how to approach this problem.
Long code example:
(defun incremental-insert-o ()
(interactive)
; init if not bound
(when (not (boundp 'iivar)) (setq iivar 0))
(cond
((and (eq last-repeatable-command 'incremental-insert-o)
(= iivar 1))
(insert "o o ")
(setq iivar 2))
((and (eq last-repeatable-command 'incremental-insert-o)
(= iivar 2))
(insert "o o o ")
(setq iivar 3))
((and (eq last-repeatable-command 'incremental-insert-o)
(= iivar 3))
(insert "o o o "))
(t
(insert "o ")
(setq iivar 1)))
)
(global-set-key [f8] 'incremental-insert-o)
Now, you're asking for more efficient code. There are a few things you could mean by this. You could mean that you want code that executes faster. How slow is the code now? When I run it on my Emacs, it's instant. Given that this code, by definition, is called from a buttonpress, it doesn't have to be super fast. Your code is more than fast enough for its use case, so I wouldn't worry about making it any faster. It also doesn't use memory: if you call it n times, it'll still only use enough memory to store one integer: this algorithm is O(1). Sounds good to me.
You could also mean "write this in fewer lines". This will also make the code less error-prone, and easier to understand. That's certainly a reasonable goal. Your code isn't awful to begin with, so it's not a necessity, but nor is it a bad idea. There are a few modifications we could make to your function. You could drop the entire third clause of your cond, and let the (= iivar 2) case be the final one, eliminating the need to set iivar to 3 there. Well, that's better already.
But wait, the function calls (eq last-repeatable-command 'incremental-insert-o) up to three times! That's a lot. Let me try to rewrite it! First, let's start with a base function definition, with an interactive call, as you have:
(defun incremental-insert-o ()
(interactive))
Now, I'm going to restructure things from your code. First, let's see if we can keep track of iivar correctly. I'm going to rename that variable to incremental-insert-o-consecutive, for readability, and because Emacs Lisp has a single namespace, so anything else using a variable named iivar will read and write to the same place your code's looking at:
(defun incremental-insert-o ()
(interactive)
(if (eq last-repeatable-command 'incremental-insert-o)
(setq incremental-insert-o-consecutive
(1+ incremental-insert-o-consecutive))
(setq incremental-insert-o-consecutive
1)))
Is that working? I'll bind it to [F8] as you did: (global-set-key [f8] 'incremental-insert-o). Now, hit [F8] to run it, but it doesn't tell you what the return value is. Let's change the function slightly to test it:
(defun incremental-insert-o ()
(interactive)
(if (eq last-repeatable-command 'incremental-insert-o)
(setq incremental-insert-o-consecutive
(1+ incremental-insert-o-consecutive))
(setq incremental-insert-o-consecutive
1))
(message "incremental-insert-o-consecutive is currently %s" incremental-insert-o-consecutive))
Hit [F8] a few times to make sure it works, and it does! It starts at 1, increases by 1 each consecutive time it's called, and resets when you do something else. Now, we just need to print out the right message. What do we want to print? Well, the first time you call the function, print out one "o ", then the second time, print out "o o ", then the third and all other times, print "o o o ". Note that printing the second string is just printing the first string twice, and the third string is printing the first string three times:
(defun incremental-insert-o ()
(interactive)
(if (eq last-repeatable-command 'incremental-insert-o)
(setq incremental-insert-o-consecutive
(1+ incremental-insert-o-consecutive))
(setq incremental-insert-o-consecutive
1))
(dotimes (i incremental-insert-o-consecutive)
(insert "o ")))
This is almost right! It does the right thing for times 1 through 3, but doesn't cap off at inserting "o o o "; it goes on to print "o o o o ", etc. So we just need to cap off the limit of repeats at 3:
(defun incremental-insert-o ()
(interactive)
(if (eq last-repeatable-command 'incremental-insert-o)
(setq incremental-insert-o-consecutive
(1+ incremental-insert-o-consecutive))
(setq incremental-insert-o-consecutive
1))
(dotimes (i (min incremental-insert-o-consecutive
3))
(insert "o ")))
Now, this seems to do exactly what you want. Let's look at the changes from the original function. This counts the number of repeats beyond 3. But the output behavior is the same, so I don't think this matters, and it seems nicer to keep the actual count of repeats. It will break if you ever overflow the integer, but that seems unlikely. Emacs guarantees at least 536870911 as MAXINT. So let's call that a day. We did get the code shorter, and have no repeated parts. I think that makes it more readable.
Here's something I could think of, however, take it with a grain of salt, because it may be overly complex, and you don't want to bring this much complexity into what you do:
(defstruct command-state
action next-state)
(defmacro define-action-states (name condition &rest actions)
(labels ((%make-command-state
(action name)
`(make-command-state :action (lambda () ,action))))
`(let ((head ,(%make-command-state (car actions) name)))
(defvar ,name nil)
(setq ,name head)
,#(loop for action in (cdr actions)
collect
`(setf (command-state-next-state ,name)
,(%make-command-state action name)
,name (command-state-next-state ,name)))
(setf (command-state-next-state ,name) head
,name head)
(defun ,(intern (concat (symbol-name name) "-command")) ()
(when ,condition
(unwind-protect
(funcall (command-state-action ,name))
(setq ,name (command-state-next-state ,name))))))))
(define-action-states print-names (= 1 1)
(message "first state")
(message "second state")
(message "third state")
(message "fourth state"))
(print-names-command)
;; will print messages looping through them,
;; each time you call it
I've made it to use a struct, so that you could add more conditions, independent of the state itself, for example, but mostly so the names would be more self-explanatory.
Also, probably, that's not the place you should really care about efficiency - so far your fingers cannot outrun the eLisp interpreter, it's all good ;)
Here's something I did to your code to possibly improve it a bit (now the worst case scenario will only check 5 conditions instead of 6 :)
(defun smart-killer ()
(interactive)
(let* ((properties (symbol-plist 'smart-killer))
(counter (plist-get properties :counter)))
(if (region-active-p)
(kill-region (region-beginning) (region-end))
(if (eq last-repeatable-command 'smart-killer)
(if (> counter 3)
(message "Kill ring is already filled with paragraph.")
(if (> counter 2)
(progn
(yank)
(kill-new "")
(mark-paragraph -1)
(kill-region (region-beginning) (region-end)))
(if (> counter 1)
(kill-region (point) (line-beginning-position))
(kill-line))))
(when (not (looking-at "\\<\\|\\>")) (backward-word)) ; begin/end of word
(kill-word 1))
(plist-put properties :counter (mod (1+ counter) 5)))))
(put 'smart-killer :counter 0)
This is what I came up with in the end:
(defun smart-killer ()
(interactive)
(cond
; [1] If region active, kill region
((region-active-p)
(kill-region (region-beginning) (region-end)))
; [2] If this command was last called, check how many times before it ran
((eq last-repeatable-command 'smart-killer)
(cond
; [2a]
((= sm-killer 1)
(kill-line))
; [2b]
((= sm-killer 2)
(kill-region (point) (line-beginning-position)))
; [2c]
((= sm-killer 3)
(yank)
(kill-new "")
(mark-paragraph -1)
(kill-region (region-beginning) (region-end)))
; [2d]
((= sm-killer 4)
(message "Kill ring is already filled with paragraph.")))
(incf sm-killer))
; [3]
(t
(when (not (looking-at "\\<\\|\\>")) (backward-word)) ; begin/end of word
(kill-word 1)
(setq sm-killer 1)))
)

Emacs cond, possible to have things happen between clauses?

I programmed some months ago some code with a lot of if statements. If region-active-p, if beginning-of-line, those kind of things.
Having learned about the cond lisp, I was wondering if I could improve my code a lot.
The problem is that this cond is only doing things when "true" as far as I see it, while I actually need the move back-to-indentation in between these checks.
In order to properly skip the last clause, I even have to set variable values.
(defun uncomment-mode-specific ()
"Uncomment region OR uncomment beginning of line comment OR uncomment end"
(interactive)
(let ((scvar 0) (scskipvar 0))
(save-excursion
(if (region-active-p)
(progn (uncomment-region (region-beginning) (region-end))
(setq scskipvar 1))
(back-to-indentation)) ; this is that "else" part that doesn't fit in cond
(while (string= (byte-to-string (following-char)) comment-start)
(delete-char 1)
(setq scskipvar 1))
(indent-for-tab-command)
(when (= scskipvar 0)
(search-forward comment-start nil t)
(backward-char 1)
(kill-line))
)))
)
So basically my question is, I would kind of like to have some consequences of not giving "true" to a clause, before the check of another clause. Is this possible? If not, what would be the best thing to do?
EDIT: Since we are using this as the example case for a solution, I wrote it down so it is easier to understand.
If region is active, remove comments from region. If not, move point to intendation.
For as long as the following character is a comment character, delete it. Afterwards, indent this line.
If it didn't do any of the above, search forward for a comment character, and kill that line.
(defun delete-on-this-line (regex)
(replace-regexp regex "" nil (line-beginning-position) (line-end-position)))
(defun delete-leading-comment-chars ()
(delete-on-this-line (eval `(rx bol (* space) (group (+ ,comment-start))))))
(defun delete-trailing-comment-chars ()
(delete-on-this-line (eval `(rx (group (+ ,comment-end)) (* space) eol))))
(defun delete-trailing-comment ()
(delete-on-this-line (eval `(rx (group (+ ,comment-start) (* anything) eol)))))
(defun uncomment-dwim ()
(interactive)
(save-excursion
(if (region-active-p)
(uncomment-region (region-beginning) (region-end))
(or (delete-leading-comment-chars)
(delete-trailing-comment-chars)
(delete-trailing-comment)))))
Edit: A little explanation:
It's a lot easier to do regex replacements than manage loops to do deletion, so that gets rid of the state. And the steps are all mutually exclusive, so you can just use or for each option.
The rx macro is a little DSL that compiles down to valid regexes, and it's also amenable to lispy syntax transforms, so I can dynamically build a regex using the comment chars for the current mode.
(defmacro fcond (&rest body)
(labels ((%substitute-last-or-fail
(new old seq)
(loop for elt on seq
nconc
(if (eql (car elt) old)
(when (cdr elt)
(error "`%S' must be the last experssion in the clause"
(car elt)))
(list new)
(list (car elt))))))
(loop with matched = (gensym)
with catcher = (gensym)
for (head . rest) in body
collect
`(when (or ,head ,matched)
(setq ,matched t)
,#(%substitute-last-or-fail `(throw ',catcher nil) 'return rest))
into clauses
finally
(return `(let (,matched) (catch ',catcher ,#clauses))))))
(macroexpand '(fcond
((= 1 2) (message "1 = 2"))
((= 1 1) (message "1 = 1"))
((= 1 3) (message "1 = 3") return)
((= 1 4) (message "1 = 4"))))
(let (G36434)
(catch (quote G36435)
(when (or (= 1 2) G36434)
(setq G36434 t)
(message "1 = 2"))
(when (or (= 1 1) G36434)
(setq G36434 t)
(message "1 = 1"))
(when (or (= 1 3) G36434)
(setq G36434 t)
(message "1 = 3")
(throw (quote G36435) nil))
(when (or (= 1 4) G36434)
(setq G36434 t)
(message "1 = 4"))))
Here's something quick to do, what I think you may be after, i.e. something that would mimic the behaviour switch in C.
The idea is that all clauses are tested sequentially for equality, and if one matches, then all following clauses are executed, until the return keyword (it would be break in C, but Lisp uses return for the similar purpose in the loop, so I thought that return would be better). The code above thus will print:
1 = 1
1 = 3
Technically, this is not how switch works in C, but it will produce the same effect.
One thing I did here for simplicity, which you want to avoid / solve differently - the use of return keyword, you probably want to impose stricter rules on how it should be searched for.
cond
Cond evaluates a series of conditions in a list, each item in a list can be a condition, and then executable instructions.
The example in the Emacs Lisp manual is adequate to demonstrate how it works, I've annotated it here to help you understand how it works.
(cond ((numberp x) x) ;; is x a number? return x
((stringp x) x) ;; is x a string? return x
((bufferp x) ;; is x a buffer?
(setq temporary-hack x) ;; set temporary-hack to buffer x
(buffer-name x)) ;; return the buffer-name for buffer x
((symbolp x) (symbol-value x))) ;; is x a symbol? return the value of x
Each part of the condition can be evaluated any way you like, the fact x above is in each condition is coincidental.
For example:
(cond ((eq 1 2) "Omg equality borked!") ;; Will never be true
(t "default")) ;; always true
So comparisons with switch are a bit limited, it's essentially a list of if statements, that executes/returns the first true condition's body list.
Hopefully this helps you understand cond a bit better.
(cond (condition body ... ) ;; execute body of 1st passing
(condition body ... ) ;; condition and return result
(condition body ... ) ;; of the final evaluation.
;; etc
)
OR
You can do things similar to switch with OR, depending on how you structure the code.
This isn't functional style, because it relies on side-effects to do what you want, then returns a boolean value for flow control, here's an example in pseudo lisp.
(or)
(or
(lambda() (do something)
(evaluate t or nil) ; nil to continue; t to quit.
)
(lambda() (do something)
(evaluate t or nil) ; nil to continue; t to quit.
)
(lambda() (do something)
(evaluate t or nil) ; nil to continue; t to quit.
)
(lambda() (do something)
(evaluate t or nil) ; nil to continue; t to quit.
)
)
Here's working example of a switch like structure using or
(or
(when (= 1 1)
(progn
(insert "hello\n")
nil))
(when (= 1 2) ;; condition fails.
(progn
(insert "hello\n")
nil)) ;; returns false (nil)
(when (= 1 1)
(progn
(insert "hello\n")
t)) ;; returns true, so we bail.
(when (= 1 1)
(progn
(insert "hello\n")
nil))
)
Inserts :
hello
hello
(and)
The and operator (not just in Lisp) is also very useful, instead of evaluating everything until true, it evaluates conditions that are true, until a false is evaluated.
Both or & and can be used to build useful logic trees.
This is how I did it now according to Chris' idea that breaking it down into seperate functions would make it easier.
EDIT: Now also applied the or knowledge gained in this thread gained from Slomojo (no more variables!)
(defun sc-uncomment ()
(interactive)
(or
(if (region-active-p)
(uncomment-region (region-beginning) (region-end))
(back-to-indentation)
nil)
(if (string= (byte-to-string (following-char)) comment-start)
(sc-check-start)
(sc-end))))
(defun sc-check-start ()
(interactive)
(while (string= (byte-to-string (following-char)) comment-start)
(delete-char 1))
)
(defun sc-end ()
(interactive)
(search-forward comment-start nil t)
(backward-char 1)
(kill-line))
)

Using ispell/aspell to spell check camelcased words

I need to spell check a large document containing many camelcased words. I want ispell or aspell to check if the individual words are spelled correctly.
So, in case of this word:
ScientificProgrezGoesBoink
I would love to have it suggest this instead:
ScientificProgressGoesBoink
Is there any way to do this? (And I mean, while running it on an Emacs buffer.) Note that I don't necessarily want it to suggest the complete alternative. However, if it understands that Progrez is not recognized, I would love to be able to replace that part at least, or add that word to my private dictionary, rather than including every camel-cased word into the dictionary.
I took #phils suggestions and dug around a little deeper. It turns out that if you get camelCase-mode and reconfigure some of ispell like this:
(defun ispell-get-word (following)
(when following
(camelCase-forward-word 1))
(let* ((start (progn (camelCase-backward-word 1)
(point)))
(end (progn (camelCase-forward-word 1)
(point))))
(list (buffer-substring-no-properties start end)
start end)))
then, in that case, individual camel cased words suchAsThisOne will actually be spell-checked correctly. (Unless you're at the beginning of a document -- I just found out.)
So this clearly isn't the fullblown solution, but at least it's something.
There is "--run-together" option in aspell. Hunspell can't check camelcased word.
If you read the code of aspell, you will find its algorithm actually does not split camelcase word into a list of sub-words. Maybe this algorithm is faster, but it will wrongly report word containing two character sub-word as typo. Don't waste time to tweak other aspell options. I tried and they didn't work.
So we got two problems:
aspell reports SOME camelcased words as typos
hunspell reports ALL camelcased words as typos
Solution to solve BOTH problems is to write our own predicate in Emacs Lisp.
Here is a sample predicate written for javascript:
(defun split-camel-case (word)
"Split camel case WORD into a list of strings.
Ported from 'https://github.com/fatih/camelcase/blob/master/camelcase.go'."
(let* ((case-fold-search nil)
(len (length word))
;; ten sub-words is enough
(runes [nil nil nil nil nil nil nil nil nil nil])
(runes-length 0)
(i 0)
ch
(last-class 0)
(class 0)
rlt)
;; split into fields based on class of character
(while (< i len)
(setq ch (elt word i))
(cond
;; lower case
((and (>= ch ?a) (<= ch ?z))
(setq class 1))
;; upper case
((and (>= ch ?A) (<= ch ?Z))
(setq class 2))
((and (>= ch ?0) (<= ch ?9))
(setq class 3))
(t
(setq class 4)))
(cond
((= class last-class)
(aset runes
(1- runes-length)
(concat (aref runes (1- runes-length)) (char-to-string ch))))
(t
(aset runes runes-length (char-to-string ch))
(setq runes-length (1+ runes-length))))
(setq last-class class)
;; end of while
(setq i (1+ i)))
;; handle upper case -> lower case sequences, e.g.
;; "PDFL", "oader" -> "PDF", "Loader"
(setq i 0)
(while (< i (1- runes-length))
(let* ((ch-first (aref (aref runes i) 0))
(ch-second (aref (aref runes (1+ i)) 0)))
(when (and (and (>= ch-first ?A) (<= ch-first ?Z))
(and (>= ch-second ?a) (<= ch-second ?z)))
(aset runes (1+ i) (concat (substring (aref runes i) -1) (aref runes (1+ i))))
(aset runes i (substring (aref runes i) 0 -1))))
(setq i (1+ i)))
;; construct final result
(setq i 0)
(while (< i runes-length)
(when (> (length (aref runes i)) 0)
(setq rlt (add-to-list 'rlt (aref runes i) t)))
(setq i (1+ i)))
rlt))
(defun flyspell-detect-ispell-args (&optional run-together)
"If RUN-TOGETHER is true, spell check the CamelCase words.
Please note RUN-TOGETHER will make aspell less capable. So it should only be used in prog-mode-hook."
;; force the English dictionary, support Camel Case spelling check (tested with aspell 0.6)
(let* ((args (list "--sug-mode=ultra" "--lang=en_US"))args)
(if run-together
(setq args (append args '("--run-together" "--run-together-limit=16"))))
args))
;; {{ for aspell only, hunspell does not need setup `ispell-extra-args'
(setq ispell-program-name "aspell")
(setq-default ispell-extra-args (flyspell-detect-ispell-args t))
;; }}
;; ;; {{ hunspell setup, please note we use dictionary "en_US" here
;; (setq ispell-program-name "hunspell")
;; (setq ispell-local-dictionary "en_US")
;; (setq ispell-local-dictionary-alist
;; '(("en_US" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_US") nil utf-8)))
;; ;; }}
(defvar extra-flyspell-predicate '(lambda (word) t)
"A callback to check WORD. Return t if WORD is typo.")
(defun my-flyspell-predicate (word)
"Use aspell to check WORD. If it's typo return t."
(let* ((cmd (cond
;; aspell: `echo "helle world" | aspell pipe`
((string-match-p "aspell$" ispell-program-name)
(format "echo \"%s\" | %s pipe"
word
ispell-program-name))
;; hunspell: `echo "helle world" | hunspell -a -d en_US`
(t
(format "echo \"%s\" | %s -a -d en_US"
word
ispell-program-name))))
(cmd-output (shell-command-to-string cmd))
rlt)
;; (message "word=%s cmd=%s" word cmd)
;; (message "cmd-output=%s" cmd-output)
(cond
((string-match-p "^&" cmd-output)
;; it's a typo because at least one sub-word is typo
(setq rlt t))
(t
;; not a typo
(setq rlt nil)))
rlt))
(defun js-flyspell-verify ()
(let* ((case-fold-search nil)
(font-matched (memq (get-text-property (- (point) 1) 'face)
'(js2-function-call
js2-function-param
js2-object-property
js2-object-property-access
font-lock-variable-name-face
font-lock-string-face
font-lock-function-name-face
font-lock-builtin-face
rjsx-text
rjsx-tag
rjsx-attr)))
subwords
word
(rlt t))
(cond
((not font-matched)
(setq rlt nil))
;; ignore two character word
((< (length (setq word (thing-at-point 'word))) 2)
(setq rlt nil))
;; handle camel case word
((and (setq subwords (split-camel-case word)) (> (length subwords) 1))
(let* ((s (mapconcat (lambda (w)
(cond
;; sub-word wholse length is less than three
((< (length w) 3)
"")
;; special characters
((not (string-match-p "^[a-zA-Z]*$" w))
"")
(t
w))) subwords " ")))
(setq rlt (my-flyspell-predicate s))))
(t
(setq rlt (funcall extra-flyspell-predicate word))))
rlt))
(put 'js2-mode 'flyspell-mode-predicate 'js-flyspell-verify)
Or just use my new pacakge https://github.com/redguardtoo/wucuo
You should parse the camel cased words and split them, then check the individual spelling for each one and assemble a suggestion taking into account the single suggestion for each misspelled token. Considering that each misspelled token can have multiple suggestions this sounds a bit inefficient to me.