1

I'm interested in finding zero-width spaces and similar "glyphless" characters in my buffer -- and more generally, text that's highlighted or has a certain face.

I can show zero-width spaces and similar things by customizing the glyphless-char-display (see this answer) or glyphless-display-mode. Those show those characters -- but I want to navigate them, with some kind of next/previous behavior, like one gets with isearch or occur.

A very similar use-case is when I've used highlight-regexp and I have something highlighted throughout my buffer -- now I want to jump to the next/previous such highlighted bit. Or see a total number of the matches.

Is there some way to do this? It seems like I would be searching/occur'ing for some kind of face/display property? Or perhaps for characters with a certain Unicode character property/category?

4 Answers 4

1

I was hoping to avoid a solution that avoids having to list all the "weird"/interesting characters -- but if I have to do that, the solution below makes it easy to modify the characters you look for.

(Part of my motivation here came from a bug related to handling U+00A0, no-break space. So one could add "NO-BREAK SPACE" to the list below.)

    (defun zero-width-occur ()
      "Run occur with a regexp matching various zero-width space, punctuation characters.
Consider using this with `glyphless-display-mode' and `whitespace-mode'.

See https://www.fileformat.info/info/unicode/block/general_punctuation/list.htm
    and 
    https://unicode-explorer.com/b/2000 
    and 
    https://www.alanwood.net/unicode/general_punctuation.html"
      (interactive)
      (occur (mapconcat (lambda (name) (char-to-string (char-from-name name)))
               '("ZERO WIDTH SPACE" ; U+200B
                 "ZERO WIDTH NON-JOINER" ; U+200C
                 "ZERO WIDTH JOINER" ; U+200D
                 "ZERO WIDTH NO-BREAK SPACE" ; U+FEFF
                 "WORD JOINER" ; U+2060
    ) "\\|")))

For the highlight-regexp stuff, here's what I found I can use with text-property-search-forward:

    (defun next-highlight ()
      (interactive)
      (defun mypredicate (value property)
        (if (listp property) (member value property))) 
      (text-property-search-forward 'face 'hi-yellow #'mypredicate))

Neither is quite what I was hoping for, but are a good first step and I hope others find them useful. Improvements welcome.

0

C-h f text-property-search:

text-property-search-forward is an autoloaded interactive byte-compiled Lisp function in text-property-search.el.

(text-property-search-forward PROPERTY &optional VALUE PREDICATE NOT-CURRENT)

Search for next region of text where PREDICATE returns non-nil for PROPERTY.

PREDICATE is used to decide whether the value of PROPERTY at a given buffer position should be considered as a match for VALUE. VALUE defaults to nil if omitted.

If PREDICATE is a function, it will be called with two arguments: VALUE and the value of PROPERTY at some buffer position. The function should return non-nil if these two values are to be considered a match.

Two special values of PREDICATE can also be used: If PREDICATE is t, that means the value of PROPERTY must equal VALUE to be considered a match. If PREDICATE is nil (which is the default), the value of PROPERTY will match if it is not equal to VALUE. Furthermore, a nil PREDICATE means that the match region ends where the value changes. For instance, this means that if you loop with

(while (setq prop (text-property-search-forward 'face)) ...)

you will get all the distinct regions with non-nil face values in the buffer, and the prop object will have the details about the match. See the manual for more details and examples about how VALUE and PREDICATE interact.

If NOT-CURRENT is non-nil, current buffer position is not examined for matches: the function will search for the first region that doesn’t include point and has a value of PROPERTY that matches VALUE.

If no matches can be found, return nil and don’t move point. If found, move point to the end of the region and return a prop-match object describing the match. To access the details of the match, use prop-match-beginning and prop-match-end for the buffer positions that limit the region, and prop-match-value for the value of PROPERTY in the region.

Other relevant functions are documented in the text-properties group. Probably introduced at or before Emacs version 27.1.

4
  • I couldn't get that to work for the glyphless characters, but for the highlighted regexps, I could: if you've used M-s h r and highlighted with the hi-yellow face, then this will move to the end of the next highlight: (defun mypredicate (value property) (if (listp property) (member value property))) and then do the search with (text-property-search-forward 'face 'hi-yellow #'mypredicate).
    – Dan Drake
    Commented Nov 11, 2023 at 19:42
  • FYI: You can post an answer to your own question, @DanDrake. (And you can accept your own answer as the most helpful.)
    – Drew
    Commented Nov 12, 2023 at 0:13
  • I can't speak for the vanilla Emacs text-property search commands. I've never used them; I just know they added them. ;-)
    – Drew
    Commented Nov 12, 2023 at 0:15
  • 1
    I added my code above to my answer -- thanks for finding text-property-search-forward for me.
    – Dan Drake
    Commented Nov 13, 2023 at 12:24
0

If you use library Highlight (highlight.el) you can use commands hlt-next-highlight and hlt-previous-highlight to navigate to the next/previous occurrence of a given face.

0

For things like zero-width spaces that have a Unicode representation, you can just use isearch or occur. The only trick is how to insert the Unicode character into the search string: C-x 8 RET does that; e.g. for zero-width space, you can say:

C-s C-x 8 RET zero width space RET C-s C-s C-s ...

and similarly for other Unicode characters, with each C-s taking you to the next occurrence.

You can also cut and paste the Unicode character (which may be easier to do if you highlight them first) into the isearch search string.

See Inserting Text in the Emacs manual.

1
  • I was hoping for something more general -- I want emacs to tell me which characters it has decided are glyphless or have zero width, etc. For example, what if I somehow get a "Mongolian vowel separator" -- unicode-explorer.com/c/180E -- in my buffer? I want to find all such "weird" characters without having to list them myself. But my answer below is at least a start.
    – Dan Drake
    Commented Nov 11, 2023 at 21:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.