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))

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


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.


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


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")))

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)))

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.