Skip to main content
6 of 10
Simplification
phils
  • 53.1k
  • 3
  • 84
  • 127

I guess you want a two-pass system then, whereby you firstly collect/count identifiers, and secondly discard those which appeared more than once, and then highlight what's left.

Here's a rough implementation...

The initial regexp "\\_<.+?\\_>" and the subsequent (regexp-opt unique 'symbols) are connected, as the 'symbols argument wraps \_< and \_> (being symbol start/end delimiters) around the result. This ensures that the latter search will only match instances of the original search. If you weren't matching symbols, you'd need to change both patterns, with appropriate care taken.

(defun my-highlight-unique-symbols ()
  "In each block, highlight all symbols which occur exactly once."
  (interactive)
  (font-lock-ensure)
  (save-excursion
    (goto-char (point-min))
    (let ((case-fold-search nil)
          (blockstart (point)))
      ;; Establish the next block.
      (while (and (not (eobp))
                  (or (search-forward "\n\n\n" nil t)
                      (goto-char (point-max))))
        (let ((blockend (point)))
          (goto-char blockstart)
          ;; Highlight unique identifiers in this block.
          (let ((identifiers (obarray-make))
                unique)
            (save-excursion
              (while (re-search-forward "\\_<.+?\\_>" blockend t)
                ;; Count the occurrences of each match.
                (let ((sym (intern (match-string 0) identifiers)))
                  (put sym 'count (1+ (or (get sym 'count) 0))))))
            ;; Extract the unique instances.
            (obarray-map (lambda (sym)
                           (when (eql (get sym 'count) 1)
                             (push (symbol-name sym) unique)))
                         identifiers)
            ;; Highlight the unique instances.
            (let ((regexp (regexp-opt unique 'symbols)))
              (save-excursion
                (with-silent-modifications
                  (while (re-search-forward regexp blockend t)
                    (put-text-property (match-beginning 0) (match-end 0)
                                       'face 'highlight)))))
            (setq blockstart blockend)))))))

Which you can run with M-x my-highlight-unique-symbols

My font-lock knowledge is poor, but I think I now have this cooperating with font-lock-mode (by virtue of calling font-lock-ensure first, so that it won't subsequently/stealthily fontify/clobber any of the face properties elsewhere in the buffer).

phils
  • 53.1k
  • 3
  • 84
  • 127