Cópia profunda de uma string no Elisp?


9

Eu tenho uma string apropriada. Quero fazer uma cópia profunda para adicionar mais propriedades, preservando as propriedades na string original. Como posso fazer isso (facilmente)?

Exemplo

Avalie um por um:

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

2
Eu reportaria isso como um erro no add-face-text-property. Não deve modificar destrutivamente a lista, pois falha quando essa lista é referida por outras pessoas.
Lindydancer


Obrigado por relatar o bug. Pena que ninguém respondeu a ele ainda. Seria bom corrigir esta função de utilitário (codificada em C).
Tirou

Respostas:


7

Você pode usar a função font-lock-append-text-propertypara adicionar a propriedade de texto. Não modifica o valor destrutivamente.

Por exemplo:

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

Aqui, em test-str-1, manteve seu valor original.


4

Descobri que você pode fazer isso iterando sobre as propriedades do texto, copiando os dados da propriedade subjacente e substituindo as propriedades existentes por novas cópias.

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

Nos meus testes, isso foi cerca de 20% mais rápido que a sua readsolução. Também escrevi uma versão que não usava um buffer temporário e modifiquei as propriedades de uma string com menos código, mas mais lenta.

Observando o código C, ele copia as listas de propriedades, com copy_sequence, que reconstruirá a estrutura da lista, mas não copiará os elementos por valor; portanto, as propriedades como face no seu exemplo que possuem um valor de lista são copiadas por referência e modificadas. Bug ou não, eu não sei


2

Você pode usar (concat the-original-string).

Por exemplo:

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

11
Não funciona, vou adicionar um exemplo.
abo-abo

11
O truque é ter uma lista aninhada nas propriedades, como eu. Então concatnão funciona.
abo-abo

@ abo-abo. Ok, agora eu vejo. Não vi isso no seu exemplo adicionado. Nesse caso, não tenho resposta, mas acho que há uma necessidade real dessa função. (Um problema potencial é que é impossível saber se uma propriedade desconhecida poderia esperar para se referir a um objeto compartilhado de algum tipo.)
Lindydancer

1

Foi encontrada uma solução alternativa (não muito eficiente):

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

2
A solução alternativa falhará se as propriedades contiverem o #caractere.
precisa

você quer dizer se o caractere # faz parte do nome do símbolo? Ou significa propriedades que são buffers ou outros dados não imprimíveis? Se for o primeiro, você deve registrar um bug.
Malabarba 24/03

buffers nas propriedades
abo-abo
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.