Search code examples
emacselispsubstringfont-lockemacs-faces

Emacs Remove text from a string based on font-face


I've got a string which I got by calling buffer-substring. Is there a way to remove text from this string based on its face settings? As a random example:

(save-excursion 
  (let ((end (point)))
(ignore-errors 
  (while (not (looking-at "[^][ \t\r\n(){}]+:"))
    (backward-sexp)))

No idea how SO is going to highlight that, but in Emacs, save-excursion, let, ignore-errors and while are all highlighted as keywords, and the regex argument to looking-at is highlighted as a string. The return value of buffer-substring looks like

#("    (save-excursion 
      (let ((end (point)))
    (ignore-errors 
      (while (not (looking-at \"[^][ \\t\\r\\n(){}]+:\"))
        (backward-sexp)))" 0 5 (fontified t) 5 19 (fontified t face font-lock-keyword-face) 19 28 (fontified t) 28 31 (fontified t face font-lock-keyword-face) 31 50 (fontified t) 50 63 (fontified t face font-lock-keyword-face) 63 65 (fontified t) 65 69 (fontified t) 69 74 (fontified t face font-lock-keyword-face) 74 75 (fontified t) 75 92 (fontified t) 92 94 (fontified t face font-lock-string-face) 94 95 (fontified t face (font-lock-negation-char-face font-lock-string-face)) 95 112 (fontified t face font-lock-string-face) 112 115 (fontified t) 115 137 (fontified t))

Given that example string, how would I go about stripping everything that has the face font-lock-keyword-face? That is, I want to do be able to do something like

(foo-bar *that-region* 'font-lock-keyword-face)

and have it return

(
  ( ((end (point)))
( 
  ( (not (looking-at "[^][ \t\r\n(){}]+:"))
    (backward-sexp)))

Solution

  • You will have to use text-property-any to find the start of the offending face and text-property-not-all to find its end. Then you will need to iterate.

    (defun kill-text-with-property (start end property value &optional object)
      "Delete the text with the PROPERTY in the part of OBJECT from START and END."
      (interactive "r\nsProperty: \nsValue: ")
      (let ((delenda ()) (here start))
        ;; collect the list of regions to kill
        ;; note that delenda contains the regions in the reverse order so that
        ;; deleting the later ones do not affect the boundaries of the ealier ones
        (while (< here end)
          (let ((beg (text-property-any here end property value object))
                (stop (and beg (text-property-not-all beg end property value object) end)))
            (if (null beg)
                (setq here end)     ; done
              (push (cons beg stop) delenda)
              (setq here stop))))
        (if (stringp object)
            ;; collect the complements of delenda into a new string
            (....)
          ;; buffer: kill the delenda regions
          (with-current-buffer (or object (current-buffer))
            (dolist (pair delenda)
              (kill-region (car pair) (cdr pair)))))))