[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [el-search] How to search string excluding docstring?
From: |
Michael Heerdegen |
Subject: |
Re: [el-search] How to search string excluding docstring? |
Date: |
Mon, 25 Dec 2017 15:56:14 +0100 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/27.0.50 (gnu/linux) |
Chunyang Xu <mail@xuchunyang.me> writes:
> Hi Michael Heerdegen and other Emacs users,
>
> For example, with the following contents in a buffer
>
> (defun foo ()
> "foo docstring"
> (message "foo string"))
>
> (string "foo") matches both "foo docstring" and "foo string", I want a
> way to exclude the docstring, by "docstring", I just mean the string
> being checking should not be the fourth element in
>
> (defun _ _ docstring . _)
>
> I am thinking a pattern like
>
> (and (pred stringp) (guard (not (docstring-p))) (string "foo"))
>
> but I have no idea how to define 'docstring-p'.
The general task is context-sensitive matching. You have no chance with
the current version of el-search, because all patterns there are not
context aware, as you have noticed.
I want to add that, but because there are some pitfalls, I've not yet
uploaded something like this.
Here is what I currently have:
#+begin_src emacs-lisp
(defun el-search--try-find-parent-beg ()
;; try to find beg of parent exp heuristically
(unless (looking-at "^(")
(let ((opoint (point)))
(or (and (search-backward-regexp "[;\(\)\"]" (max (- (point) 300)
(point-min)))
(looking-at "\(")
(and (or (eobp)) (not (= (char-before) ?\\)))
(not (looking-back ";" (line-beginning-position))))
(progn (goto-char opoint)
nil)))))
(el-search-defpattern parent (pattern &optional n)
"Matches when PATTERN matches the (Nth) parent of the current expression.
N defaults to 1. Slow."
(let ((parent-end (make-symbol "parent-end"))
(counter (make-symbol "ctr")))
`(and
(let ,counter (or ,n 1))
(let (,'\` ((,'\, ,pattern)))
(save-excursion
(condition-case nil
(progn
(while (>= (cl-decf ,counter) 0)
(or
(el-search--try-find-parent-beg)
(cond
((eq (char-before) ?`) (backward-char 1))
((eq (char-before) ?,) (backward-char 1))
((and (eq (char-before (1- (point))) ?,)
(eq (char-before) ?@))
(backward-char 2))
((eq (char-before) ?') (backward-char 1))
((and (eq (char-before (1- (point))) ?#)
(eq (char-before) ?'))
(backward-char 2))
(t (when-let* ((,parent-end (scan-lists (point) 1 1)))
(goto-char ,parent-end)
(backward-list))))))
(list (read (current-buffer))))
(scan-error nil)))))))
#+end_src
That should enable you to solve your task. Note it's experimental, and
I only had a quick look again now - want to go hiking!
One thing you can definitely _not_ do with this is to use it recursively
- i.e. (parent (parent PATTERN)) won't work (that's why I added the
optional N argument) - `parent' is only valid when applied to the
current expression.
Please tell me if it works for you.
BTW, another not yet uploaded pattern for matching strings you may find
useful is
#+begin_src emacs-lisp
(el-search-defpattern string-lines (pattern)
"Match any string whose line number is matched by PATTERN.
Examples: (string-lines 1) matches one-line strings.
\(string-lines (pred (>= 5))\) matches strings consisting of not
more than 5 lines."
(let ((string (make-symbol "string")))
`(and (string)
,string
(let ,pattern
(with-temp-buffer
(insert ,string)
(count-lines (point-min) (point-max)))))))
#+end_src
I don't know if it's useful enough to upload it.
Regards,
Michael.