Menyalin string di Elisp?

9

Saya memiliki string propertized. Saya ingin membuat salinan yang dalam untuk menambahkan lebih banyak properti, sambil mempertahankan properti dalam string asli. Bagaimana saya bisa melakukan itu (dengan mudah)?

Contoh

Evaluasi satu per satu:

(setq test-str-1
      #(";; This `is' a test"
        0 3 (fontified nil face font-lock-comment-delimiter-face)
        3 9 (fontified nil face font-lock-comment-face)
        9 11 (fontified nil face (font-lock-constant-face font-lock-comment-face))
        11 19 (fontified nil face font-lock-comment-face)))
(setq test-str-2 (concat test-str-1))
(add-face-text-property 0 (length test-str-2) 'foobar t test-str-2)

Dan hasilnya:

test-str-2
;; =>
#(";; This `is' a test" 0 3 (fontified nil face (font-lock-comment-delimiter-face foobar))
  3 9 (fontified nil face (font-lock-comment-face foobar))
  9 11 (fontified nil face (font-lock-constant-face font-lock-comment-face foobar))
  11 19 (fontified nil face (font-lock-comment-face foobar)))
test-str-1
;; =>
#(";; This `is' a test" 0 3 (face font-lock-comment-delimiter-face fontified nil)
  3 9 (face font-lock-comment-face fontified nil)
  9 11 (face (font-lock-constant-face font-lock-comment-face foobar) ; <= foobar is here
        fontified nil)
  11 19 (face font-lock-comment-face fontified nil))
abo-abo
sumber
2
Saya akan melaporkan ini sebagai bug di add-face-text-property. Seharusnya tidak merusak daftar, karena gagal ketika daftar itu disebut oleh orang lain.
Lindydancer
1
Oke, laporkan bug di debbugs.gnu.org/cgi/bugreport.cgi?bug=20153
abo-abo
Terima kasih telah melaporkan bug. Sayang sekali belum ada yang merespons. Akan lebih baik untuk memperbaiki fungsi utilitas ini (kode C).
Drew

Jawaban:

7

Anda dapat menggunakan fungsi ini font-lock-append-text-propertyuntuk menambahkan properti teks. Itu tidak mengubah nilai secara destruktif.

Sebagai contoh:

(setq test-str-1
      #(";; This `is' a test"
        0 3 (fontified nil face font-lock-comment-delimiter-face)
        3 9 (fontified nil face font-lock-comment-face)
        9 11 (fontified nil face (font-lock-constant-face font-lock-comment-face))
        11 19 (fontified nil face font-lock-comment-face)))
(setq test-str-2 (concat test-str-1))
(font-lock-append-text-property 0 (length test-str-2) 'face '(foobar t) test-str-2)


test-str-1
#(";; This `is' a test"
  0 3 (face font-lock-comment-delimiter-face fontified nil)
  3 9 (face font-lock-comment-face fontified nil)
  9 11 (face (font-lock-constant-face font-lock-comment-face) fontified nil)
  11 19 (face font-lock-comment-face fontified nil))

test-str-2
#(";; This `is' a test"
  0 3 (fontified nil face (font-lock-comment-delimiter-face foobar t))
  3 9 (fontified nil face (font-lock-comment-face foobar t))
  9 11 (fontified nil face (font-lock-constant-face font-lock-comment-face foobar t))
  11 19 (fontified nil face (font-lock-comment-face foobar t)))

Di sini, di test-str-1, telah mempertahankan nilai aslinya.

Lindydancer
sumber
4

Saya menemukan Anda dapat melakukan ini dengan mengulangi properti teks, menyalin data properti yang mendasarinya dan menimpa properti yang ada dengan salinan baru.

(defun deep-copy-text-properties (str)
  (with-temp-buffer
    (insert str)
    (goto-char 1)
    (while (not (eobp))
      (set-text-properties (point)
                           (goto-char (next-char-property-change (point) (point-max)))
                           ;; copy-tree is the important part
                           (copy-tree (text-properties-at (1- (point))))))
    (buffer-string)))

Dalam pengujian saya, ini sekitar 20% lebih cepat dari readsolusi Anda . Saya juga menulis versi yang tidak menggunakan buffer temp dan memodifikasi properti string yang lebih sedikit kode tetapi lebih lambat.

Melihat kode C itu menyalin daftar properti, dengan copy_afterence yang akan membangun kembali struktur daftar tetapi tidak menyalin elemen dengan nilai, sehingga properti seperti wajah dalam contoh Anda yang memiliki nilai daftar disalin oleh referensi dan dimodifikasi. Bug atau tidak, saya tidak tahu

Jordon Biondo
sumber
2

Anda bisa menggunakannya (concat the-original-string).

Sebagai contoh:

(let ((s "TEXT"))
  (set-text-properties 2 3 '(:foreground "blue") s)
  (let ((q (concat s)))
    (add-text-properties 2 3 '(:background "red") q)
    (cons s q)))
;; Returns:
(#("TEXT" 2 3 (:foreground "blue")) . #("TEXT" 2 3 (:foreground "blue" :background "red")))
Lindydancer
sumber
1
Tidak berfungsi, saya akan menambahkan contoh.
abo-abo
1
Kuncinya adalah memiliki daftar bersarang di properti, seperti yang saya lakukan. Maka concattidak berhasil.
abo-abo
@ abo-abo. Ok, sekarang saya mengerti. Saya tidak melihatnya dalam contoh tambahan Anda. Dalam hal ini saya tidak punya jawaban, tapi saya pikir ada kebutuhan nyata untuk fungsi seperti itu. (Satu masalah potensial adalah bahwa tidak mungkin untuk mengetahui apakah properti yang tidak dikenal mungkin berharap untuk merujuk pada objek bersama dari beberapa jenis.)
Lindydancer
1

Menemukan solusi (tidak sangat efisien):

(setq test-str-2
      (read (prin1-to-string test-str-1)))
abo-abo
sumber
2
Work-around gagal jika proprties berisi #karakter.
abo-abo
maksud Anda jika karakter # adalah bagian dari nama simbol? Atau apakah properti jahat yang merupakan buffer atau data yang tidak dapat dicetak lainnya? Jika ini yang pertama, Anda harus mengajukan bug.
Malabarba
buffer dalam properti
abo-abo