How to distinguish between different overlays at point - emacs

ORIGINAL QUESTION:  I'm looking for some assistance, please, to distinguish between overlays that may exist at point. If the overlays-at point is made with the lawlist-red-face then do X. If the overlays-at point is made with calendar-holiday-marker, then do Y. The overlays are made with these two functions.
(calendar-mark-visible-date (car holiday) lawlist-red-face)
(calendar-mark-visible-date (car holiday) calendar-holiday-marker)
EDIT (January 1, 2014):  #Drew wrote a nice test for this in calendar+.el ( http://www.emacswiki.org/emacs/calendar%2B.el ):
(when
(memq lawlist-red-face
(mapcar (function (lambda (ovr)
(overlay-get ovr 'face))) (overlays-at (point))))
... )
EDIT (February 13, 2014):  The above-listed snippet can also be used in conjunction with something like (while (re-search-backward "[0-9]" nil t) to create a combined list of dates with overlays -- month, day and year are extracted with calendar-extract-day, calendar-extract-month and calendar-extract-year -- the date is obtained with (calendar-cursor-to-nearest-date):
;; modified with the help of #phils based on comments down below.
(setq lawlist-mouse-marked
(append lawlist-mouse-marked
`((holiday-sexp '(list ,month ,day ,year) ""))))
That list can then be used with something like calendar-holiday-list (or the modified code below) to remark dates on a new calendar layout after it has been generated. This is useful if the user has manually marked dates (e.g., with the mouse) and wants those dates to reappear after scrolling the calendar forwards / backwards. The library holidays.el contains the function holiday-sexp which uses a filtering function holiday-filter-visible-calendar to edit the list of dates so that only those that are visible on the new calendar get marked:
(dolist (holiday
(let (res h err)
(sort
(dolist (p lawlist-mouse-marked res)
(if (setq h (eval p))
(setq res (append h res))))
'calendar-date-compare)))
(calendar-mark-visible-date (car holiday) lawlist-mouse-calendar-face))

Ok, let's try to figure this out (without really knowing anything about the calendar).
(overlays-at) returns a list of overlays. I will keep this simple and only handle the first, if you want to examine all of them, simply loop over them until you find one that is suitable.
Also, the face property can be more complex than simply the name of the face, if that is the case you would need to handle that as well.
But anyway, here is a simple piece of code that (hopefully) does what you want:
(let ((overlays (overlays-at (point))))
(if overlays
(let ((face (overlay-get (car overlays) 'face)))
(cond ((eq face 'lawlist-red-face)
;; Do something
)
((eq face 'holiday)
;; Do another thing
)
(t
;; Do something else)))))

Related

How to correctly pass arguments on Common Lisp?

I'm currently reading Practical Common Lisp, and I decided to try and improve its first example (simple database), just to practice.
I'm trying to make the update function more generic, like the select function.
Here's my code, it runs but doesn't actually update anything like it should.
(defun make-if-expr (field value)
`(if ,value (setf (getf row ,field) ,value)))
(defun make-if-list (fields)
(loop while fields
collecting (make-if-expr (pop fields) (pop fields))))
(defun update (selector-fn &rest clauses)
(setf *db*
(mapcar
#'(lambda (row)
(when (funcall selector-fn row)
(apply #'make-if-list clauses))
row)
*db*)))
This is modified from the original source code, available in the link.
As an example, using ((:TITLE "Title" :ARTIST "Artist" :RATING 7 :RIPPED T)) as a database, typing in (update (where :title "Title") (list :title "New Title")) in the REPL should change the title to "New Title". It doesn't, though.
There is no error, no nothing. It just doesn't work.
Since I'm a complete beginner at Common Lisp, can anyone tell me what am I doing wrong? I'm pretty sure I'm not passing arguments to functions correctly.
MAKE-IF-LIST creates a list of lists and returns it. You are not doing anything with them and thus nothing happens.
It's also not clear why you are creating the lists, since you could just write a function which does the update.
This is not directly related to your problem, but:
(defun make-if-expr (field value)
`(if ,value (setf (getf row ,field) ,value)))
... injects value at two different places, which means it is going to be evaluated twice when non-nil. This is problematic if computing the value takes time or if it performes side-effects. You should probably introduce a variable to hold that value:
(defun make-if-expr (field value)
(let ((symbol (gensym)))
`(let ((,symbol ,value))
(if ,symbol (setf (getf row ,field) ,symbol)))))
I suppose you are trying to make update work similiarly to how where was implemented with a macro at the end of the chapter. In that case, what you're doing wrong is simply that your update is not a macro. You need to change it into one, and use the backtick syntax to evaluate the necessary parts (selector-fn and (make-if-list ...)):
(defmacro update (selector-fn &rest clauses)
`(setf *db*
(mapcar
#'(lambda (row)
(when (funcall ,selector-fn row)
,#(make-if-list clauses))
row)
*db*)))
(setf *db* (list (list :TITLE "Title" :ARTIST "Artist" :RATING 7 :RIPPED T)))
(update (where :title "Title") :title "New Title")
*db*
; => ((:TITLE "New Title" :ARTIST "Artist" :RATING 7 :RIPPED T))
Edit: As Rainer Joswig said in the comments, the code above does have some problems. In reality you would have to make sure you don't evaluate variables multiple times, and you shouldn't use symbols that may be used by the surrounding code. The book should cover that in later chapters, but a better, although a bit more complicated, version of the code would look like this (Also see coredumps answer, since I didn't want to steal that into this):
;; You need to pass the `row-sym` here, because this can't
;; rely on the row being called `row` (which it isn't).
;; You'll also want to see the answer by coredump regarding this.
(defun make-if-expr (field value row-sym)
`(if ,value (setf (getf ,row-sym ,field) ,value)))
(defun make-if-list (fields row-sym)
(loop while fields
collecting (make-if-expr (pop fields) (pop fields) row-sym)))
(defmacro update (selector-fn &rest clauses)
;; You need to use `gensym` to generate unique names
;; for all symbols you use in the expanded code.
(let ((selector-sym (gensym "selector-fn"))
(row-sym (gensym "row")))
;; Assign `selector-fn` into a variable. This avoids the
;; function being built by the `where` macro multiple times.
`(let ((,selector-sym ,selector-fn))
(setf *db*
(mapcar
;; You have to use the unique symbols generated above,
;; so the `*-sym` variables are evaluated with a comma.
#'(lambda (,row-sym)
(when (funcall ,selector-sym ,row-sym)
,#(make-if-list clauses row-sym))
,row-sym)
*db*)))))
Thanks to the wonderful tips everyone gave me, I was able to solve my problem.
I was trying to generate the ifs inside the update function, which only makes sense using macros. Instead, I used a single function now.
This is my new code, hopefully it will be useful for people having similar problems:
(defun update-if (field value row)
(setf (getf row field) value))
(defun update-if-list (fields row)
(loop while fields
do (update-if (pop fields) (pop fields) row)))
(defun update (selector-fn &rest clauses)
(setf *db*
(mapcar
#'(lambda (row)
(when (funcall selector-fn row)
(update-if-list clauses row))
row)
*db*)))

Could Emacs fontify elisp string constants?

The Dilemma: readability or maintainability?
Let's look at the following function.
It doesn't really matter what it does, the important part is that
it's using twice the string "(let\\*?[ \t]*":
(defun setq-expression-or-sexp ()
"Return the smallest list that contains point.
If inside VARLIST part of `let' form,
return the corresponding `setq' expression."
(interactive)
(ignore-errors
(save-excursion
(up-list)
(let ((sexp (preceding-sexp)))
(backward-list 1)
(cond
((looking-back "(let\\*?[ \t]*")
(cons 'setq
(if (= (length sexp) 1)
(car sexp)
(cl-mapcan
(lambda (x) (unless (listp x) (list x nil)))
sexp))))
((progn
(up-list)
(backward-list 1)
(looking-back "(let\\*?[ \t]*"))
(cons 'setq sexp))
(t
sexp))))))
Since it's a headache having to update the string in two (or more) locations,
I'd have to defconst it like so:
(defconst regex-let-form "(let\\*?[ \t]*")
Although the code became more maintainable, it became less readable as well,
because it's hard to see at a glance what regex-let-form really is:
(defun setq-expression-or-sexp ()
"Return the smallest list that contains point.
If inside VARLIST part of `let' form,
return the corresponding `setq' expression."
(interactive)
(ignore-errors
(save-excursion
(up-list)
(let ((sexp (preceding-sexp)))
(backward-list 1)
(cond
((looking-back regex-let-form)
(cons 'setq
(if (= (length sexp) 1)
(car sexp)
(cl-mapcan
(lambda (x) (unless (listp x) (list x nil)))
sexp))))
((progn
(up-list)
(backward-list 1)
(looking-back regex-let-form))
(cons 'setq sexp))
(t
sexp))))))
The idea: why not both?
Since it's a constant anyway, why not font-lock it
and make regex-let-form appear as if it's "(let\\*?[ \t]*"?
It's a feasable job, since:
It's possible to font-lock identifiers like so: http://www.emacswiki.org/emacs/PrettyLambda,
or even so: rainbow-mode.
And it's possible to font-lock constants. It's already done for c++-mode,
but not yet for emacs-lisp-mode, as far as I know.
Then it remains only to connect the two. Unfortunately, I don't know
enough of font-lock innards to do it, but maybe someone else does?
Or is there already a package that does this?
Tweaking the code from this answer,
I've solved the problem:
(font-lock-add-keywords
'emacs-lisp-mode
'((fl-string-constant . 'font-lock-constant-face)) 'append)
(defun fl-string-constant (_limit)
(while (not
(ignore-errors
(save-excursion
(skip-chars-forward "'")
(let ((opoint (point))
(obj (read (current-buffer)))
obj-val)
(and (symbolp obj)
(risky-local-variable-p obj)
(special-variable-p obj)
(stringp (setq obj-val (eval obj)))
(progn
(put-text-property
(1- (point)) (point) 'display
(format "%c\"%s\"" (char-before) obj-val))
(set-match-data (list opoint (point)))
t))))))
(if (looking-at "\\(\\sw\\|\\s_\\)")
(forward-sexp 1)
(forward-char 1)))
t)
This displays the value of a string constant right after the constant name.
It works quite nicely with fontified string constants as well.
Speed is a bit of an issue - suggestions to improve are welcome.
Also, I couldn't find anything better than risky-local-variable-p to determine
that it's a constant. The doc says that defconst marks the variable
as special and risky, but nothing else.
hl-defined.el (updated today, 2013-10-20) can highlight constant Emacs-Lisp symbols as such, that is, variables whose current value is the symbol itself. If your defconst has been evaluated then this will do what you are requesting.
This seems to work (source: http://www.emacswiki.org/emacs/PrettyLambda):
(font-lock-add-keywords 'emacs-lisp-mode
`(("\\<\\(regex-let-form\\)\\>" (0 (prog1 nil
(compose-region (match-beginning 1)
(match-end 1)
"\"(let\\\\*?[ \\t]*\""))))))
Although I think adding regex-let-form into the existing let block would be a cleaner solution:
(let ((sexp (preceding-sexp))
(regex-let-form "(let\\*?[ \t]*"))
...
Perhaps your example is not indicative of the real problem, and you really do want to do some display replacement or font-locking, as you say.
But I will answer wrt your example and the problem as posed, regarding maintainability vs readability: Just let-bind your regexp. The binding, unlike a defconst will be nearby and clearly related to the occurrences of the bound variable.
This is typically what people do. Again, you might have had another use case in mind --- I am responding only to the problem as posed narrowly.

How to get face from under overlay?

Here's the situation: I need to retrieve the face's under point boundaries, but if I use highlight-current-line mode, that face overlays the face that I'm interested in.
face-at-point or (get-char-property (point) 'face) will only give me the first face in the list, and it will be the one from the current-line overlay. How to get the underlying faces?
EDIT:
This is more or less what I ended up doing:
(defun haxe-face-at-point ()
"This is like `face-at-point' except we will only look for faces
which are relevant to haxe-mode. This will also look under overlays
created by minor modes like ispel or highlight current line."
(interactive)
(let ((props (text-properties-at (point))))
(catch 't
(while props
(when (eql (car props) 'face)
(throw 't
(when (member (cadr props)
'(font-lock-string-face
font-lock-keyword-face
font-lock-variable-name-face
font-lock-comment-face
font-lock-preprocessor-face
font-lock-type-face
default))
(cadr props))))
(setq props (cdr props))))))
I only needed to find out if there's one of the list anyways.
There are sadly no good facilities provided to Elisp code for that. The best I can offer you is to use overlays-at and then loop through the result, using overlay-get to see which ones of the overlays specifies a face and finally use get-text-property to get the face specified by the text-properties (if any). The display engine combines all those.

matching keys in association lists in emacs lisp

I'm using folding-mode in emacs and was trying to make a function to insert the appropriate folding marker (start or end) depending on mode. So far I have
(defun insert-folding-mode-mark ()
(interactive)
(let ((st "##{{{")
(en "##}}}")
string-to-insert)
(save-excursion
(setq string-to-insert
(let ((here (point))
sp ep)
(setq sp (search-backward st))
(goto-char here)
(setq ep (search-backward en))
(if (< sp ep) st en))))
(insert string-to-insert)))
This inserts "##{{{" at (point) unless "##{{{" precedes it, in which case it inserts "##}}}".
I'd like to replace the first (let) assignment with something that determines the start and end markers with something like
(let* ((match (assoc (intern mode-name) folding-mode-marks-alist))
(st (nth 1 match))
(en (nth 2 match)))
[is (intern) meant to be called in this way?] A truncated version of my folding-mode-marks-alist looks something like
((ess-mode "##{{{" "##}}}")
(tex-mode "%{{{" "%}}}")
(python-mode "# {{{" "# }}}")
(emacs-lisp-mode ";;{{{" ";;}}}")
(TeX-mode "%{{{" "%}}}")
(LaTeX-mode "%{{{" "%}}}"))
while the mode-name returned from various modes are {"Emacs-Lisp", "ESS[S]", "PDFLaTeX", "Python", ...}. Seems like I might want to do some partial matching with strings using (downcase), (concat x "-mode"), and so on, but was wondering if there was an idiomatic way in emacs lisp to do this sort of matching with keys of an alist, or do I just have to have a separate block of code by which I extract the keys with (mapcar 'car folding-mode-marks-alist) and convert each symbol to string (how?) to do the matching?
Thanks much!
Emacs Lisp has a destructuring-bind facility which may be helpful here. Also taking advantage of the fact that the symbol naming the current major mode is available via the variable major-mode, you can write something like this:
(destructuring-bind (st en) (cdr (assoc major-mode folding-mode-marks-alist))
; do stuff
)
Note that this won't work if (assoc major-mode folding-mode-marks-alist) returns nil, so better replace that with a call to some custom function capable of returning a sensible default.
In addition to major-mode being more appropriate than mode-name here, the function insert-folding-mode-mark as listed above would throw an error if there were no folding marker between cursor and beginning of buffer. Here is a revision without that quirk:
(require 'cl)
(defun insert-folding-mode-mark ()
(interactive)
(flet ((fn (s) (save-excursion (or (search-backward s () t) 0))))
(destructuring-bind (mode st en)
(or (assoc major-mode folding-mode-marks-alist) '(nil "" ""))
(insert (if (<= (fn st) (fn en)) st en)))))
Edit: fix problem pointed out in comment.
You may be interested to know there are comment-start and comment-end variables which should already contain the information you need based on major-mode. Something like
(search-backward (concat comment-start "{{{"))
...
(insert comment-start "{{{" comment-end)
should be sufficient. Of course, for lisp modes comment-start is ";" so you may want to do what you are doing to get ";;" but fall back on comment-start for other modes. You can also (setq comment-start ";;") though I'm not entirely sure what difference that makes for the lisp modes.

Help with an interactive Emacs Lisp function for replacing text

I've been using Emacs for a couple months now, and I want to get started in elisp programming. Specifically, I'd like to write my own interactive function. However, I'm more than a bit lost. (interactive ...) has tons of options and I'm not sure which one I want. Then, I don't really know the names of the functions I need. If someone could kindly help me turn my pseudocode into real code, I would be mighty appreciative! (And as always, any links to informative places would be good. Right now I've just been reading this.)
Here is pseudocode for what I'd like to do:
(defun my-func (buffer) ; I think I need the buffer as an arg?
"does some replacements"
(interactive ???) ; ?
(let (replacements (list
'("a-regexp-string" . "a-replacement-string-with-backreferences")
...)) ; more of the above
(while replacements
(let (current (car replacements)) ; get a regexp-replacement pair
(some-regexp-replace-func buffer (car current) (cdr current)) ; do the replacement
(setq replacements (cdr replacements))))))
First, from the looks of your function you would probably be doing it in the current buffer, so no, you don't need to have a 'buffer' argument. If that's a bad assumption, I can change the code. Next, in a 'let' if you are assigning to variables you need another set of parens around each pair of var/value. Finally, when looping through a list I prefer to use functional-programming-like functions (mapcar, mapc, etc.). I'll try to inline some comments here:
(defun my-func ()
"Do some replacements"
(interactive)
(let ((replacements (list '("foo" . "bar")
'("baz" . "quux"))))
(save-excursion ; So point isn't moved after this function
(mapc (lambda (x) ; Go through the list, with this 'inline' function
; being called with each element as the variable 'x'
(goto-char (point-min)) ; Start at the beginning of the buffer
(while (re-search-forward (car x) nil t) ; Search for the car of the replacement
(replace-match (cdr x)))) ; And replace it with the cdr
replacements)))) ; The list we're mapc'ing through
As for what to read, I'd suggest the Elisp manual that comes with Emacs.