Copia profunda una cadena en Elisp?

9

Tengo una cadena de propiedad. Quiero hacer una copia profunda de él para agregar más propiedades, conservando las propiedades en la cadena original. ¿Cómo puedo hacer eso (fácilmente)?

Ejemplo

Evaluar uno por 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)

Y el resultado:

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
fuente
2
Informaría esto como un error en add-face-text-property. No debe modificar destructivamente la lista, ya que falla cuando otros hacen referencia a esa lista.
Lindydancer
1
OK, informó el error en debbugs.gnu.org/cgi/bugreport.cgi?bug=20153
abo-abo el
Gracias por informar del error. Lástima que nadie haya respondido todavía. Sería bueno arreglar esta función de utilidad (codificada en C).
Dibujó el

Respuestas:

7

Puede usar la función font-lock-append-text-propertypara agregar la propiedad de texto. No modifica el valor de forma destructiva.

Por ejemplo:

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

Aquí, en test-str-1, ha conservado su valor original.

Lindydancer
fuente
4

Descubrí que puede hacer esto iterando sobre las propiedades del texto, copiando los datos de propiedad subyacentes y sobrescribiendo las propiedades existentes con nuevas copias.

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

En mis pruebas, esto fue aproximadamente un 20% más rápido que su readsolución. También escribí una versión que no usaba un búfer temporal y modifiqué las propiedades de una cadena que tenía menos código pero era más lenta.

Al mirar el código C, copia las listas de propiedades, con copy_sequence que reconstruirá la estructura de la lista pero no copiará los elementos por valor, por lo que las propiedades como la cara en su ejemplo que tienen un valor de lista se copian por referencia y se modifican. Error o no, no lo sé

Jordon Biondo
fuente
2

Puedes usar (concat the-original-string).

Por ejemplo:

(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
fuente
1
No funciona, agregaré un ejemplo.
abo-abo
1
El truco es tener una lista anidada en las propiedades, como lo hago yo. Entonces concatno funciona.
abo-abo
@ abo-abo. Ok, ahora ya veo. No vi eso en tu ejemplo añadido. En ese caso no tengo respuesta, pero creo que existe una necesidad real de tal función. (Un problema potencial es que es imposible saber si una propiedad desconocida podría referirse a un objeto compartido de algún tipo).
Lindydancer
1

Se encontró una solución alternativa (no muy eficiente):

(setq test-str-2
      (read (prin1-to-string test-str-1)))
abo-abo
fuente
2
La solución temporal falla si las propiedades contienen el #personaje.
abo-abo
¿quieres decir si el carácter # es parte del nombre del símbolo? ¿O significan propiedades que son buffers u otros datos no imprimibles? Si es el primero, debe presentar un error.
Malabarba
amortiguadores en propiedades
abo-abo