Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
;;; tprops.el -- dealing with text with properties and saving it as sexps ;; ;; Author: Eduardo Ochs <edrx@mat.puc-rio.br> ;; Version: 2004aug25 ;; URL: http://angg.twu.net/elisp/trops.el ;; ;; This doesn't do much right now, but check the demos at the end. ;; Note that they won't work if you're in font-lock mode: font-lock ;; fontifies everything in his own way, so you'll lose the tprops ;; faces. ;; ;; (find-elnode "Saving Properties") ;; (find-elnode "Format Conversion") ;; (find-elnode "Changing Properties" "Function: propertize") ;; (find-elnode "Special Properties") ;; (find-node "(cl)Loop Basics") ;; (load "tprops.el") ;; The data types. We deal with eight data types, named ;; ;; r0 r1 r2 ;; r0s r1s r2s ;; r0sc r2sc ;; ;; meaning: ;; ;; r0: "representation 0": an emacs string with constant properties ;; r0s: a list of r0's ;; r0sc: a series of r0's, concatenated; an emacs string with properties ;; r1: "representation 1": a string consed to a property plist ;; r1s: a list of r1's ;; r2: "representation 2": an r1 converted to text ;; r2s: a list of r2's ;; r2sc: a list of r2's, concatenated (possibly with newlines) ;; ;; Examples: ;; ;; r0: #("el" 0 2 (face fg:yellow)) ;; r0s: ("H" #("el" 0 2 (face fg:yellow)) "lo") ;; r0sc: #("Hello" 0 1 nil 1 3 (face fg:yellow) 3 5 nil) ;; r1: ("el" face fg:yellow) ;; r1s: (("H") ("el" face fg:yellow) ("lo")) ;; r2: "(\"el\" face fg:yellow)" ;; r2s: ("(\"H\")" "(\"el\" face fg:yellow)" "(\"lo\")") ;; r2sc: "(\"H\")\n(\"el\" face fg:yellow)\n(\"lo\")\n" ;; Converting from internal (r0) to external (r2): ;; (defun tp-r0-region-to-r1 (s e) (interactive "r") (cons (buffer-substring-no-properties s e) (text-properties-at s))) (defun tp-r0sc-region-to-r1s (s e) (interactive "r") (let ((nextpos (next-property-change s nil e))) (if (and nextpos (< nextpos e)) (cons (tp-r0-region-to-r1 s nextpos) (tp-r0sc-region-to-r1s nextpos e)) (if (< s e) (list (tp-r0-region-to-r1 s e)))))) (defun tp-r1s-to-r2sc (r1s) (mapconcat (lambda (r1) (format "%S\n" r1)) r1s "")) ;; Converting from external (r2) to internal (r0): ;; (defun tp-r2sc-to-r1s (r2sc) (read (concat "(" r2sc ")"))) (defun tp-r1-to-r0 (r1) (if (cdr r1) (apply 'propertize r1) (car r1))) (defun tp-r1s-to-r0sc (r1s) (mapconcat 'tp-r1-to-r0 r1s "")) ;; r2sc buffer -> r0sc buffer ;; (defun tp-r2sc-region-to-r0sc-buffer (buffername s e) (if (get-buffer buffername) (error "tp-make-r0-buffer: buffer %S exists" buffername)) (let* ((r2sc (buffer-substring-no-properties s e)) (r1s (tp-r2sc-to-r1s r2sc)) (r0sc (tp-r1s-to-r0sc r1s)) (b-f-c-s buffer-file-coding-system)) (with-current-buffer (get-buffer-create buffername) (set (make-local-variable 'buffer-file-coding-system) b-f-c-s) (insert r0sc)))) (defun tp-r0ize-buffer (buffername &optional s e) (tp-r2sc-region-to-r0sc-buffer buffername (or s (point-min)) (or e (point-max))) (switch-to-buffer buffername)) ;; Saving the contents of a r0sc buffer into the r2sc buffer that generated it ;; (defun tp-end-of-initial-comments () (save-excursion (goto-char (point-min)) (while (and (not (eobp)) (looking-at "[ \t]*\\(;\\|$\\)")) (forward-line 1)) (point))) (defun tp-beginning-of-final-comments () (save-excursion (goto-char (point-max)) (forward-line 0) (let ((pos (point-max))) (while (and (not (bobp)) (looking-at "[ \t]*\\(;\\|$\\)")) (setq pos (point)) (forward-line -1)) pos))) (defun tp-replace-r2sc-block (newr2sc) (let ((s (tp-end-of-initial-comments)) (e (tp-beginning-of-final-comments))) (if (> s e) (error "No r2sc part found!")) (delete-region s e) (goto-char s) (insert newr2sc))) (defun tp-r2ize-buffer (buffername) (let* ((r1s (tp-r0sc-region-to-r1s (point-min) (point-max))) (r2sc (tp-r1s-to-r2sc r1s))) (switch-to-buffer buffername) (tp-replace-r2sc-block r2sc))) ;; Demos ;; (info "(elisp)Special Properties") (make-face 'fg:yellow) (set-face-foreground 'fg:yellow "yellow") (setq tp-sample-r1s '( ("a" face (:foreground "red")) (" ") (":=" face bold) (" ") ("2" face fg:yellow) )) (setq tp-hello-r1s '( ("H") ("el" face fg:yellow) ("lo") )) ;; The tp-r1s--to-r0sc demo won't work if you're in font-lock-mode ' (insert (tp-r1s-to-r0sc tp-sample-r1s)) ' (insert (tp-r1s-to-r2sc tp-sample-r1s)) ;; An application: ;; (defun tp-filter-props (filter plist) (if plist (let ((propkey (car plist)) (propval (cadr plist)) (rest (cddr plist))) (append (funcall filter propkey propval) (tp-filter-props filter rest))))) (defun tp-r1s-filter-props (filter r1s) (mapcar (lambda (r1) (cons (car r1) (tp-filter-props filter (cdr r1)))) r1s)) (defun tp-r1s-propkeys (r1s) (let ((propkeys ())) (tp-r1s-filter-props (lambda (key val) (add-to-list 'propkeys key)) r1s) propkeys)) (defun tp-r0sc-region-propkeys (s e show-it) (interactive "r\np") (let* ((max-lisp-eval-depth 500000) (max-specpdl-size 50000) (r1s (tp-r0sc-region-to-r1s (min s e) (max s e))) (propkeys (tp-r1s-propkeys r1s))) (if show-it (message "%S" propkeys)) propkeys)) ;; r0 files <-> r2 files ;; (defun tp-read-r2-file (fname) "Destroy the contents of the current file & rebuild them (r0<-r2) from FNAME" (let (r1s coding (thisbuffer (current-buffer)) (pos (point))) (find-file fname) (setq r1s (read (buffer-substring (point-min) (point-max)))) (setq coding buffer-file-coding-system) (switch-to-buffer thisbuffer) (delete-region (point-min) (point-max)) (set-buffer-file-coding-system coding) (insert (tp-r1s-to-r0sc r1s)) (goto-char pos))) (defun tp-save-into-r2-file (fname) "Destroys most of FNAME and stores this file there, doing r0->r2" (let* ((max-lisp-eval-depth 500000) (max-specpdl-size 500000) (thisbuffer (current-buffer)) (r1s (tp-r0sc-region-to-r1s (point-min) (point-max)))) (find-file fname) (goto-char (tp-end-of-initial-comments)) (delete-region (point) (point-max)) (save-excursion (insert "(\n" (tp-r1s-to-r2sc r1s) ")\n")) (switch-to-buffer thisbuffer))) ;; tp-r0-mode and tp-r2-mode ;; (defvar tp-r0-file nil "The name of the associated tp-r0 file (for tp-r2 buffers)") (defvar tp-r2-file nil "The name of the associated tp-r2 file (for tp-r0 buffers)") (defvar tp-r0-status nil "True in a tp-r0 buffer if we have executed tp-read-r2-file") (make-variable-buffer-local 'tp-r0-file) (make-variable-buffer-local 'tp-r2-file) (make-variable-buffer-local 'tp-r0-status) (defun tp-r0-visit-r2-file () (interactive) (find-file tp-r2-file)) (defun tp-r0-assert-properties () (interactive) (if (null tp-r0-status) (tp-r0-read-r2-file) (message "Properties already read"))) (defun tp-r0-read-r2-file () (interactive) (let ((bm (buffer-modified-p))) (tp-read-r2-file tp-r2-file) (if (not bm) (set-buffer-modified-p nil))) (setq tp-r0-status t) (message "Read %s" tp-r2-file)) (defun tp-r0-save-properties () (interactive) (tp-save-into-r2-file tp-r2-file) (message "Wrote %s" tp-r2-file)) (defvar tp-r0-mode-map (make-sparse-keymap)) (define-key tp-r0-mode-map "\C-c2" 'tp-r0-visit-r2-file) (define-key tp-r0-mode-map "\C-c\C-o" 'tp-r0-visit-r2-file) (define-key tp-r0-mode-map "\C-c\C-a" 'tp-r0-assert-properties) (define-key tp-r0-mode-map "\C-c\C-r" 'tp-r0-read-r2-file) (define-key tp-r0-mode-map "\C-c\C-s" 'tp-r0-save-properties) (define-minor-mode tp-r0-mode "Mode for editing a tp-r0 file" :init-value nil :global nil :lighter " r0") (defun tp-r2-visit-r0-file () (interactive) (find-file tp-r0-file) (tp-r0-assert-properties)) (defun tp-r2-write-r0-file () (interactive) (let ((thisfname (buffer-file-name)) (thisbuffer (current-buffer))) (find-file tp-r0-file) (tp-read-r2-file thisfname) (normal-mode) (switch-to-buffer thisbuffer))) (defvar tp-r2-mode-map (make-sparse-keymap)) (define-key tp-r2-mode-map "\C-c0" 'tp-r2-visit-r0-file) (define-key tp-r2-mode-map "\C-c\C-o" 'tp-r2-visit-r0-file) (define-key tp-r2-mode-map "\C-c\C-s" 'tp-r2-write-r0-file) (define-minor-mode tp-r2-mode "Mode for editing a tp-r2 file" :init-value nil :global nil :lighter " r2") (provide 'tprops) ;; (defun tpes-buffer-initial-comments () ;; (save-excursion ;; (goto-char (point-min)) ;; (re-search-forward "^\\([ \t]*\\(;[^\n]*\\)?\n\\)*" nil t) ;; (match-string 0))) ;; Local Variables: ;; coding: raw-text-unix ;; ee-comment-prefix: ";;" ;; modes: (emacs-lisp-mode fundamental-mode) ;; End: