all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Alan Mackenzie <acm@muc.de>
To: Davis Herring <herring@lanl.gov>
Cc: emacs-devel@gnu.org
Subject: Re: Is there a "selective setq locator/highlighter" anywhere? There is now!
Date: Fri, 23 Jan 2009 15:58:57 +0000	[thread overview]
Message-ID: <20090123155857.GE3056@muc.de> (raw)
In-Reply-To: <42394.128.165.123.18.1232647588.squirrel@webmail.lanl.gov>

Hi, Davis!

On Thu, Jan 22, 2009 at 10:06:28AM -0800, Davis Herring wrote:
> > I would like a tool which would highlight these:

> >     (setq foo bar
> >           c-state-cache (cdr c-state-cache))
> >     (setcar c-state-cache (caar c-state-cache))

> > , but not this:

> >     (setq old-cache c-state-cache)

> Not a polished tool, by any means, but can't you just do

[ .... ]

> Wrap in defuns/commands as desired.

Thanks!  In the end, I just threw it together, and it rapidly converged
to something functional.  It handles setq, setc[ad]r and let\*?,
arbitrarily nested.  For some reason, I was thinking of a solution which
would first read (as in read/eval/print loop) a defun and then step
through the structure.  This was silly.  ;-)  Anyhow, here it is for
anybody interested.  It uses hi-lock-mode a little bit:

#########################################################################
;; Selective setq highlighting:
;;
;; Given a symbol, locate and/or highlight all places where the symbol's value
;; is changed; currently this means "(setq foo" (including multiple versions
;; of it), "(setcar foo" or "(setcdr foo", or "(let (...(foo ..)" or
;; "(let*...".

(defun sshi-forward-WS ()
  (save-match-data
    (forward-comment 1048575)
    (search-forward-regexp "[[:space:]]*")))

(defun sshi-sym-value ()
  "Parse the symbol followed by a sexp at point.
Return the positions of the symbol/sexp as a dotted pair of
dotted pairs like this
    ((SYM-START . SYM-END) . (SEXP-START . SEXP-END)).
Point is left after any WS/comments at sexp-end.
On error, throw an error."
  (let (sym-pos sexp-start)
    (if (looking-at "\\(\\(\\w\\|\\s_\\)+\\_>\\)") ; a symbol.
	;;             1  2            2       1
	(progn
	  (setq sym-pos (cons (match-beginning 1) (match-end 1)))
	  (goto-char (match-end 0))
	  (sshi-forward-WS)
	  (setq sexp-start (point))
	  (condition-case nil
	      (forward-sexp)
	    (error
	     (error "sshi-sym-value: invalid sexp at %s" (point)))))
      (error "sshi-sym-value: missing symbol at %s" (point)))
    (prog1 (cons sym-pos (cons sexp-start (point)))
      (sshi-forward-WS))))

(defun sshi-push-sym-sexps (sym places)
  "  Push the locations of SYM settings onto PLACES, returning PLACES.
This includes any setq's etc. recursively contained in the sexp.
Point should be at a symbol in a \"setq\" type construct.  Point
is left after WS/comments after the sexp."
  (let ((sym-val (sshi-sym-value)))
    (if (string=
	 (buffer-substring-no-properties (caar sym-val) (cdar sym-val))
	 sym)
	(push (car sym-val) places))
    (when (eq (char-after (cadr sym-val)) ?\()
      (goto-char (cadr sym-val))
      (setq places
	    (append (save-restriction
		      (narrow-to-region (cadr sym-val) (cddr sym-val))
		      (sshi-list sym))
		    places))
      (sshi-forward-WS))
    places))

(defun sshi-list (sym)
  "Return a list of places within the current restriction where SYM is set.
This is a list of dotted pairs of the form (BEGIN-SYM . END-SYM).

The current restriction should exactly contain a list, and point
should be at (point-min) on entry.  Point is left at (point-max)
at exit."
  (let (places sym-start)
    (forward-char)			; over ?\(
    (sshi-forward-WS)
    (while (/= (char-after) ?\))
      (cond
       ((looking-at "(\\(setc[ad]r\\_>\\)")
	(goto-char (match-end 0)) (sshi-forward-WS)
	(setq places (sshi-push-sym-sexps sym places))
	(sshi-forward-WS)
	(forward-char)			; over ?\)
	(sshi-forward-WS))

       ((looking-at "(\\(setq\\_>\\)")
	(goto-char (match-end 0)) (sshi-forward-WS)
	(while (/= (char-after) ?\))
	  (setq places (sshi-push-sym-sexps sym places))
	  (sshi-forward-WS))
	(forward-char) (sshi-forward-WS)) ; over ?\)

       ((looking-at "(\\(let\\*?\\_>\\)")
	(goto-char (match-end 0)) (sshi-forward-WS)
	(or (eq (char-after) ?\()
	    (error "sshi: missing bindings list at %s" (point)))
	(forward-char) (sshi-forward-WS) ; over ?\(
	(while (/= (char-after) ?\))
	  (if (eq (char-after) ?\()	; binding with initialisation
	      (progn
		(forward-char) (sshi-forward-WS) ; over ?\( of a single binding.
		(setq places (sshi-push-sym-sexps sym places))
		(forward-char))		; over terminating ?\) of the binding
	    (setq sym-start (point))  ; symbol (initialised implicitly to nil)
	    (forward-sexp)
	    (if (string= (buffer-substring sym-start (point)) sym)
		(push (cons sym-start (point)) places))
	    (sshi-forward-WS)))
	(forward-char) (sshi-forward-WS)) ; over ?\) enclosing all bindings
	
       ((eq (char-after) ?\()
	(mark-sexp)
	(save-restriction
	  (narrow-to-region (point) (mark))
	  (setq places (append (sshi-list sym) places)))
	(sshi-forward-WS))

       (t (forward-sexp) (sshi-forward-WS))))
    (forward-char)			; over ?\)
    places))

(defvar sshi-symbol-hist nil
  "The symbol history list used by selective-setq")
(defun sshi-defun (arg sym face)
  "Highlight SYM each place within the current defun where it is setq'd or setc[ad]r'd.
With a prefix arg, remove the highlighting for SYM."
  (interactive
   (list
    current-prefix-arg
    (let* ((sym (symbol-at-point))
	   (s (and sym (symbol-name sym))))
      (read-string
       "Symbol: "				; prompt
       s					; initial-input
       'sshi-symbol-hist			; history
       s))
    (unless current-prefix-arg (hi-lock-read-face-name))))

  (save-excursion
    (save-restriction
      (narrow-to-defun)
      ;(beginning-of-defun)
      (goto-char (point-min))	       ; beginning-of-defun drops a mark.  :-(
      (let ((places (sshi-list sym)))
	(mapc
	 (lambda (elt)
	   (let* (ov ovs)
	     (setq ovs (overlays-at (car elt)))
	     (while (and ovs
			 (setq ov (car ovs))
			 (not (overlay-get ov 'sshi)))
	       (setq ovs (cdr ovs)))
	     (when ovs
	       (delete-overlay ov))
	     (unless arg
	       (setq ov (make-overlay (car elt) (cdr elt) nil nil nil))
	       (overlay-put ov 'sshi t)
	       (overlay-put ov 'face face))))
	 places)))))

(define-key emacs-lisp-mode-map "\C-xwz" 'sshi-defun)
#########################################################################
> Davis

-- 
Alan Mackenzie (Nuremberg, Germany).




      reply	other threads:[~2009-01-23 15:58 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-01-22 13:54 Is there a "selective setq locator/highlighter" anywhere? Alan Mackenzie
2009-01-22 13:44 ` Lennart Borgman
2009-01-22 16:23   ` Stefan Monnier
2009-01-23 15:41   ` Alan Mackenzie
2009-01-23 15:36     ` Lennart Borgman
2009-01-23 16:12       ` Lennart Borgman
2009-01-22 14:49 ` Stefan Monnier
2009-01-22 18:06 ` Davis Herring
2009-01-23 15:58   ` Alan Mackenzie [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090123155857.GE3056@muc.de \
    --to=acm@muc.de \
    --cc=emacs-devel@gnu.org \
    --cc=herring@lanl.gov \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.