help-gnu-emacs
[Top][All Lists]
Advanced

[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.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]