Copia profonda di una stringa in Elisp?


9

Ho una stringa corretta. Voglio fare una copia profonda di esso per aggiungere più proprietà, preservando le proprietà nella stringa originale. Come posso farlo (facilmente)?

Esempio

Valuta uno per uno:

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

E il risultato:

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
Vorrei segnalare questo come un bug in add-face-text-property. Non dovrebbe modificare in modo distruttivo l'elenco, poiché fallisce quando tale elenco viene indicato da altri.
Lindydancer,


Grazie per aver segnalato il bug. Peccato che nessuno abbia ancora risposto. Sarebbe bene ottenere questa funzione di utilità (codificata in C) riparata.
Estratto l'

Risposte:


7

È possibile utilizzare la funzione font-lock-append-text-propertyper aggiungere la proprietà text. Non modifica il valore in modo distruttivo.

Per esempio:

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

Qui, in test-str-1, ha mantenuto il suo valore originale.


4

Ho scoperto che puoi farlo ripetendo le proprietà del testo, copiando i dati delle proprietà sottostanti e sovrascrivendo le proprietà esistenti con nuove copie.

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

Nei miei test, questo era circa il 20% più veloce della tua readsoluzione. Ho anche scritto una versione che non utilizzava un buffer temporaneo e modificava le proprietà di una stringa che era meno codice ma era più lenta.

Guardando il codice C copia le pianificazioni delle proprietà, con copy_sequence che ricostruirà la struttura dell'elenco ma non copierà gli elementi per valore, quindi le proprietà come face nel tuo esempio che hanno un valore di elenco vengono copiate per riferimento e modificate. Bug o no, non lo so


2

È possibile utilizzare (concat the-original-string).

Per esempio:

(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
Non funziona, aggiungerò un esempio.
abo-ABO

1
Il trucco è avere un elenco nidificato nelle proprietà, come faccio io. Quindi concatnon funziona.
a bordo del

@ abo-abo. Ok, ora vedo. Non l'ho notato nel tuo esempio aggiunto. In tal caso non ho una risposta, ma penso che ci sia una reale necessità per una tale funzione. (Un potenziale problema è che è impossibile sapere se una proprietà sconosciuta potrebbe aspettarsi di riferirsi a un oggetto condiviso di qualche tipo.)
Lindydancer

1

Trovato un aggancio (non molto efficiente):

(setq test-str-2
      (read (prin1-to-string test-str-1)))

2
La soluzione alternativa non riesce se le proprietà contengono il #carattere.
a bordo del

vuoi dire se il carattere # fa parte del nome del simbolo? O significano proprietà che sono buffer o altri dati non stampabili? Se è il primo, dovresti presentare un bug.
Malabarba,

buffer in proprietà
abo-abo
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.