Skip to main content
Clean-ups following the refactoring
Source Link
phils
  • 53.1k
  • 3
  • 84
  • 127
(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 ((regexp "\\_<.+?\\_>")
          (case-fold-search nil)
          (blockstart (point))
          (blockdelimiter "\n\n\n"))
      ;; Establish the next block.
      (while (and (not (eobp))
                  (or (search-forward blockdelimiter nil t)
                      (goto-char (point-max))))
        (let ((blockend (point)))
          (goto-char blockstart)
          ;; Highlight unique identifiers in this block.
          (let ((identifiers (obarray-make)))
            (savegoto-excursion
   char blockstart)
          (while (re-search-forward regexp blockend t)
                ;; Have we seen this matchidentifier before?
                (let* ((sym (intern (match-string 0) identifiers))
                       (seen (get sym 'region)))
                  (if seen
                      ;; Mark it as a duplicate.
                      (unless (eq seen 'duplicate)
                        (put sym 'region 'duplicate))
                    ;; This is the first time we've seen this matchidentifier.
                    ;; Store the region boundaries for the match.
                    (put sym 'region (cons (match-beginning 0)
                                           (match-end 0)))))))
            ;; Highlight the unique instances.
            (save-excursion
              (with-silent-modifications
                (obarray-map
                 (lambda (sym)
                   (let ((region (get sym 'region)))
                     (unless (eq region 'duplicate)
                       (put-text-property (car region) (cdr region)
                                          'face 'highlight))))
                 identifiers)))
            ;; Prepare for the next block.
            (setq blockstart blockend)
          (goto-char blockstart)))))) 

(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 ((regexp "\\_<.+?\\_>")
          (case-fold-search nil)
          (blockstart (point))
          (blockdelimiter "\n\n\n"))
      ;; Establish the next block.
      (while (and (not (eobp))
                  (or (search-forward blockdelimiter nil t)
                      (goto-char (point-max))))
        (let ((blockend (point)))
          (goto-char blockstart)
          ;; Highlight unique identifiers in this block.
          (let ((identifiers (obarray-make)))
            (save-excursion
              (while (re-search-forward regexp blockend t)
                ;; Have we seen this match before?
                (let* ((sym (intern (match-string 0) identifiers))
                       (seen (get sym 'region)))
                  (if seen
                      ;; Mark it as a duplicate.
                      (unless (eq seen 'duplicate)
                        (put sym 'region 'duplicate))
                    ;; This is the first time we've seen this match.
                    ;; Store the region boundaries for the match.
                    (put sym 'region (cons (match-beginning 0)
                                           (match-end 0)))))))
            ;; Highlight the unique instances.
            (save-excursion
              (with-silent-modifications
                (obarray-map
                 (lambda (sym)
                   (let ((region (get sym 'region)))
                     (unless (eq region 'duplicate)
                       (put-text-property (car region) (cdr region)
                                          'face 'highlight))))
                 identifiers)))
            ;; Prepare for the next block.
            (setq blockstart blockend)))))))
(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 ((regexp "\\_<.+?\\_>")
          (case-fold-search nil)
          (blockstart (point))
          (blockdelimiter "\n\n\n"))
      ;; Establish the next block.
      (while (and (not (eobp))
                  (or (search-forward blockdelimiter nil t)
                      (goto-char (point-max))))
        (let ((blockend (point))
              (identifiers (obarray-make)))
          (goto-char blockstart)
          (while (re-search-forward regexp blockend t)
            ;; Have we seen this identifier before?
            (let* ((sym (intern (match-string 0) identifiers))
                   (seen (get sym 'region)))
              (if seen
                  ;; Mark it as a duplicate.
                  (unless (eq seen 'duplicate)
                    (put sym 'region 'duplicate))
                ;; This is the first time we've seen this identifier.
                ;; Store the region boundaries for the match.
                (put sym 'region (cons (match-beginning 0)
                                       (match-end 0))))))
          ;; Highlight the unique instances.
          (with-silent-modifications
            (obarray-map
             (lambda (sym)
               (let ((region (get sym 'region)))
                 (unless (eq region 'duplicate)
                   (put-text-property (car region) (cdr region)
                                      'face 'highlight))))
             identifiers))
          ;; Prepare for the next block.
          (setq blockstart blockend)
          (goto-char blockstart)))))) 

added 20 characters in body
Source Link
phils
  • 53.1k
  • 3
  • 84
  • 127
(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 ((regexp "\\_<.+?\\_>")
          (case-fold-search nil)
          (blockstart (point))
          (blockdelimiter "\n\n\n"))
      ;; Establish the next block.
      (while (and (not (eobp))
                  (or (search-forward blockdelimiter nil t)
                      (goto-char (point-max))))
        (let ((blockend (point)))
          (goto-char blockstart)
          ;; Highlight unique identifiers in this block.
          (let ((identifiers (obarray-make)))
            (save-excursion
              (while (re-search-forward regexp blockend t)
                ;; Have we seen this match before?
                (let* ((sym (intern (match-string 0) identifiers))
                       (regionseen (get sym 'region)))
                  (if regionseen
                      ;; ThisMark isit as a duplicate.
                      (unless (eq regionseen 'duplicate)
                        (put sym 'region 'duplicate))
                    ;; This is the first time we've seen this match.
                    ;; Store the region boundaries for the match.
                    (put sym 'region (cons (match-beginning 0)
                                           (match-end 0)))))))
            ;; Highlight the unique instances.
            (save-excursion
              (with-silent-modifications
                (obarray-map
                 (lambda (sym)
                   (let ((region (get sym 'region)))
                     (unless (eq region 'duplicate)
                       (put-text-property (car region) (cdr region)
                                          'face 'highlight))))
                 identifiers)))
            ;; Prepare for the next block.
            (setq blockstart blockend)))))))
(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 ((regexp "\\_<.+?\\_>")
          (case-fold-search nil)
          (blockstart (point))
          (blockdelimiter "\n\n\n"))
      ;; Establish the next block.
      (while (and (not (eobp))
                  (or (search-forward blockdelimiter nil t)
                      (goto-char (point-max))))
        (let ((blockend (point)))
          (goto-char blockstart)
          ;; Highlight unique identifiers in this block.
          (let ((identifiers (obarray-make)))
            (save-excursion
              (while (re-search-forward regexp blockend t)
                ;; Have we seen this match before?
                (let* ((sym (intern (match-string 0) identifiers))
                       (region (get sym 'region)))
                  (if region ;; This is a duplicate.
                      (unless (eq region 'duplicate)
                        (put sym 'region 'duplicate))
                    ;; This is the first time we've seen this match.
                    ;; Store the region boundaries for the match.
                    (put sym 'region (cons (match-beginning 0)
                                           (match-end 0)))))))
            ;; Highlight the unique instances.
            (save-excursion
              (with-silent-modifications
                (obarray-map
                 (lambda (sym)
                   (let ((region (get sym 'region)))
                     (unless (eq region 'duplicate)
                       (put-text-property (car region) (cdr region)
                                          'face 'highlight))))
                 identifiers)))
            ;; Prepare for the next block.
            (setq blockstart blockend)))))))
(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 ((regexp "\\_<.+?\\_>")
          (case-fold-search nil)
          (blockstart (point))
          (blockdelimiter "\n\n\n"))
      ;; Establish the next block.
      (while (and (not (eobp))
                  (or (search-forward blockdelimiter nil t)
                      (goto-char (point-max))))
        (let ((blockend (point)))
          (goto-char blockstart)
          ;; Highlight unique identifiers in this block.
          (let ((identifiers (obarray-make)))
            (save-excursion
              (while (re-search-forward regexp blockend t)
                ;; Have we seen this match before?
                (let* ((sym (intern (match-string 0) identifiers))
                       (seen (get sym 'region)))
                  (if seen
                      ;; Mark it as a duplicate.
                      (unless (eq seen 'duplicate)
                        (put sym 'region 'duplicate))
                    ;; This is the first time we've seen this match.
                    ;; Store the region boundaries for the match.
                    (put sym 'region (cons (match-beginning 0)
                                           (match-end 0)))))))
            ;; Highlight the unique instances.
            (save-excursion
              (with-silent-modifications
                (obarray-map
                 (lambda (sym)
                   (let ((region (get sym 'region)))
                     (unless (eq region 'duplicate)
                       (put-text-property (car region) (cdr region)
                                          'face 'highlight))))
                 identifiers)))
            ;; Prepare for the next block.
            (setq blockstart blockend)))))))
No need for a second search when we identifier the match regions the first time around
Source Link
phils
  • 53.1k
  • 3
  • 84
  • 127

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 ((regexp "\\_<.+?\\_>")
          (case-fold-search nil)
          (blockstart (point))
          (blockdelimiter "\n\n\n"))
      ;; Establish the next block.
      (while (and (not (eobp))
                  (or (search-forward "\n\n\n"blockdelimiter 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 "\\_<.+?\\_>"regexp blockend t)
                ;; Count theHave occurrenceswe ofseen eachthis match. before?
                (letlet* ((sym (intern (match-string 0) identifiers)))
                  (put sym 'count (1+  (orregion (get sym 'count) 0)))'region)))
                  (if region ;; ExtractThis theis uniquea instancesduplicate.
                      (obarray-mapunless (lambdaeq region 'duplicate)
                        (put sym 'region 'duplicate))
                    ;; This is the first time we've (whenseen (eqlthis (getmatch.
 sym 'count) 1)
                 ;; Store the region boundaries for the match.
               (push     (symbol-nameput sym) unique))'region (cons (match-beginning 0)
                         identifiers                  (match-end 0)))))))
            ;; Highlight the unique instances.
            (letsave-excursion
 ((regexp             (regexpwith-optsilent-modifications
 unique 'symbols)))
              (saveobarray-excursionmap
                 (with-silent-modificationslambda (sym)
                   (whilelet (re-search-forward(region regexp(get blockendsym t'region)))
                     (unless (eq region 'duplicate)
                       (put-text-property (match-beginningcar 0region) (match-endcdr 0region)
                                          'face 'highlight))))
                 identifiers)))
            ;; Prepare for the next block.
            (setq blockstart blockend)))))))

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)))))))
(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 ((regexp "\\_<.+?\\_>")
          (case-fold-search nil)
          (blockstart (point))
          (blockdelimiter "\n\n\n"))
      ;; Establish the next block.
      (while (and (not (eobp))
                  (or (search-forward blockdelimiter nil t)
                      (goto-char (point-max))))
        (let ((blockend (point)))
          (goto-char blockstart)
          ;; Highlight unique identifiers in this block.
          (let ((identifiers (obarray-make)))
            (save-excursion
              (while (re-search-forward regexp blockend t)
                ;; Have we seen this match before?
                (let* ((sym (intern (match-string 0) identifiers))
                       (region (get sym 'region)))
                  (if region ;; This is a duplicate.
                      (unless (eq region 'duplicate)
                        (put sym 'region 'duplicate))
                    ;; This is the first time we've seen this match.
                    ;; Store the region boundaries for the match.
                    (put sym 'region (cons (match-beginning 0)
                                           (match-end 0)))))))
            ;; Highlight the unique instances.
            (save-excursion
              (with-silent-modifications
                (obarray-map
                 (lambda (sym)
                   (let ((region (get sym 'region)))
                     (unless (eq region 'duplicate)
                       (put-text-property (car region) (cdr region)
                                          'face 'highlight))))
                 identifiers)))
            ;; Prepare for the next block.
            (setq blockstart blockend)))))))
Bounty Ended with 100 reputation awarded by effectfully
Comments
Source Link
phils
  • 53.1k
  • 3
  • 84
  • 127
Loading
Simplification
Source Link
phils
  • 53.1k
  • 3
  • 84
  • 127
Loading
Use font-lock-ensure to avoid conflicts
Source Link
phils
  • 53.1k
  • 3
  • 84
  • 127
Loading
Formatting
Source Link
phils
  • 53.1k
  • 3
  • 84
  • 127
Loading
Remove debug code
Source Link
phils
  • 53.1k
  • 3
  • 84
  • 127
Loading
Various improvements. Especially performance / not calling regexp-opt within a loop condition... oops :)
Source Link
phils
  • 53.1k
  • 3
  • 84
  • 127
Loading
Source Link
phils
  • 53.1k
  • 3
  • 84
  • 127
Loading