Re: Show/Don't show lines based on pattern...

"Stephen J. Turnbull" <[email protected]> Tue, 10 May 2005 12:18:45 +0900
Newsgroups gmane.emacs.xemacs.design
Organization The XEmacs Project
Message-ID <[email protected]>
Since you expressed willingness to repost, I'll just forward this to
xemacs-design (which is where new features should be discussed).  If
you think this is a good direction, feel free to discuss there.  Since
your current platform is GNU Emacs, feel free to add emacs-devel at
any time.  (I think they're in the process of beating a release into
shape, so they may ask you to wait a bit on a new feature.  For that
reason you may want to get your proposal into shape before posting
there, but that's up to you.)

Any oddity is due to my cutting context, not Matthew's post!

>>>>> "Matthew" == Matthew Miner <[email protected]> writes:

    Matthew> I'd already found hideshow and outline mode. The problem
    Matthew> is that they work very well with structured files (i.e.,
    Matthew> those whose content and syntax is "well understood"), but
    Matthew> not-at-all with unstructured (e.g., text) files.

Nothing is going to work with _truly_ unstructured text (e.g., James
Joyce's _Ulysses_), if you see what I mean.  The log files you're
talking about have "record-per-line" structure, and you want to filter
on a field in the record.  I don't know if it matters that there is
structure internal to the line; you might think about that (eg, for
the purpose of hiding the date field---if you've already filtered down
to one date, it's redundant, right?)

    Matthew> selectively display relevant lines (or hide irrelevant
    Matthew> lines), expand and contract hidden sections, etc. Thus
    Matthew> minimizing clutter, but providing context where desired.

I'm not sure how to implement (ie, user interface and efficiency) the
expand/contract feature, but basically what you want is the
"invisible" text property.  Here's a primitive implementation for
XEmacs (in GNU Emacs you would use overlays or text properties):

(defun filter-matching-lines (regexp mode tag)
  "Hide or restrict to lines matching REGEXP according to MODE.

  REGEXP is a regular expression which should be anchored at both ends.
Ie, to hide \"hideme\", use \"^.*hideme.*$\".
  MODE is one of the symbols 'hide or 'restrict-to.  [with obvious semantics]
If MODE is nil, an appropriate extent will be tagged, but no action taken.
  TAG is a symbol which will be added to the 'filter-matching-lines-tagset
property of matching extents.  If TAG is nil, then MODE will be applied
globally regardless of existing filtering.

  (interactive "sFilter (a regexp): \nSMode: \nSTag: ")

  (unless (string-match "^\\^[^^$]*\\$$" regexp)
    ;; GNU Emacs doesn't have structured errors
    (error 'invalid-regexp "must be anchored" regexp))

  ;; I guess we should respect narrowing ... please let me know
  (save-excursion            ; oops, forgot this, so indent is missing
  (goto-char (point-min))
  (while (not (eobp))
    (let ((bol (point))
          (eol (let ((x (point-at-eol))) (if (< x (point-max) (1+ x) x))))
          (e (extent-at bol nil 'filter-matching-lines-tagset))
          (match-p (looking-at regexp)))
      (if e
          ;; canonicalize in case it used to be unterminate line at buffer end
          (set-extent-endpoints e bol eol)
        (setq e (make-extent bol eol))
        ;; I don't know if this placeholder is needed
        (set-extent-property e 'filter-matching-lines-tagset (nil)))
      (when tag
        (let ((set (extent-property e 'filter-matching-lines-tagset)))
        (set-extent-property e 'filter-matching-lines-tagset
                             (cons tag e (delq nil (delq tag set))))))
      (if (not match-p)
          (cond ((eq mode 'hide) (set-extent-property e 'invisible nil))
                ((eq mode 'restrict-to) (set-extent-property e 'invisible t)))
        (cond ((eq mode 'restrict-to) (set-extent-property e 'invisible nil))
              ((eq mode 'hide) (set-extent-property e 'invisible t)))))
    (forward-line))))

(defun filter-lines (predicate mode)
  "Hide or restrict display to lines satisfying PREDICATE according to MODE.

PREDICATE is function taking one argument, a list of symbols.
MODE is a symbol, one of 'hide or 'restrict-to."

  (interactive "xFilter (a lambda expr): \nMode: ")

  (save-excursion
    (goto-char (point-min))
      (while (not (eobp))
        (let* ((e (extent-at bol nil 'filter-matching-lines-tagset))
               ;; kludge -- will do the wrong thing if no property is set
               ;; but can't just error (suppose just one new line?)
               (ts (when e (extent-property e 'filter-matching-lines-tagset))))
          (if (not (funcall predicate ts))
            (cond ((eq mode 'hide) (set-extent-property e 'invisible nil))
                  ((eq mode 'restrict-to) (set-extent-property e 'invisible t)))
        (cond ((eq mode 'restrict-to) (set-extent-property e 'invisible nil))
              ((eq mode 'hide) (set-extent-property e 'invisible t)))))
    (forward-line))))

Untested, but it might work.  Very preliminary, but I think it should
be clear where this is going.

Also, the Gnus package has code for "washing" article buffers.  There
are functions for hiding quoted lines, etc.  I don't know how generic
they are, though.  And I find Gnus code hard to abstract, so I just
wrote up the above.  However, if this is a useful facility to
abstract, we might want to propose that Gnus use it, too.

    Matthew> If you feel this (or the next) is appropriate for group
    Matthew> posting, feel free or tell me and I'll repost.

In general you should always reply to the group.  If the person who
responded first doesn't feel like answering again, somebody else may
pick up the thread.

    Matthew> I often need the name of the current buffer name (or file
    Matthew> name) for use in a keystroke macro.

I don't know of any standard way to do that kind of thing.

    Matthew> I finally wrote:

    Matthew> (defun insert-buffer-name-into-kill-ring ()

Using the kill ring in keyboard macros is kind of hazardous.

Why not

(defun insert-buffer-name ()
  (interactive)
  (insert (buffer-name)))

?


-- 
School of Systems and Information Engineering http://turnbull.sk.tsukuba.ac.jp
University of Tsukuba                    Tennodai 1-1-1 Tsukuba 305-8573 JAPAN
               Ask not how you can "do" free software business;
              ask what your business can "do for" free software.