Eine Zeichenfolge in Elisp tief kopieren?

9

Ich habe eine besetzte Zeichenfolge. Ich möchte eine tiefe Kopie davon erstellen, um weitere Eigenschaften hinzuzufügen, während die Eigenschaften in der ursprünglichen Zeichenfolge erhalten bleiben. Wie kann ich das (leicht) machen?

Beispiel

Nacheinander bewerten:

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

Und das Ergebnis:

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
quelle
2
Ich würde dies als Fehler melden add-face-text-property. Die Liste sollte nicht destruktiv geändert werden, da dies fehlschlägt, wenn andere auf diese Liste verweisen.
Lindydancer
1
OK, meldete den Fehler unter debbugs.gnu.org/cgi/bugreport.cgi?bug=20153
abo-abo
Vielen Dank, dass Sie den Fehler gemeldet haben. Schade, dass noch niemand darauf geantwortet hat. Es wäre gut, diese Dienstprogrammfunktion (in C codiert) zu reparieren.
Drew

Antworten:

7

Mit der Funktion können Sie die Texteigenschaft font-lock-append-text-propertyhinzufügen. Der Wert wird nicht destruktiv geändert.

Beispielsweise:

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

Hier hat in test-str-1seinen ursprünglichen Wert beibehalten.

Lindydancer
quelle
4

Ich habe festgestellt, dass Sie dies tun können, indem Sie die Texteigenschaften durchlaufen, die zugrunde liegenden Eigenschaftsdaten kopieren und die vorhandenen Eigenschaften mit neuen Kopien überschreiben.

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

In meinen Tests war dies ungefähr 20% schneller als Ihre readLösung. Ich habe auch eine Version geschrieben, die keinen temporären Puffer verwendet und die Eigenschaften einer Zeichenfolge geändert hat, die weniger Code, aber langsamer war.

Wenn Sie sich den C-Code ansehen, werden die Eigenschaftslisten mit copy_sequence kopiert, wodurch die Listenstruktur neu erstellt wird, die Elemente jedoch nicht nach Wert kopiert werden. Daher werden die Eigenschaften wie face in Ihrem Beispiel mit einem Listenwert als Referenz kopiert und geändert. Bug oder nicht, ich weiß es nicht

Jordon Biondo
quelle
2

Sie können verwenden (concat the-original-string).

Beispielsweise:

(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
quelle
1
Funktioniert nicht, ich werde ein Beispiel hinzufügen.
Abo-Abo
1
Der Trick besteht darin, wie ich eine verschachtelte Liste in den Eigenschaften zu haben. Dann concatgeht das nicht.
Abo-Abo
@ abo-abo. Ok, jetzt verstehe ich. Das habe ich in Ihrem hinzugefügten Beispiel nicht bemerkt. In diesem Fall habe ich keine Antwort, aber ich denke, es besteht ein wirklicher Bedarf für eine solche Funktion. (Ein mögliches Problem ist, dass es unmöglich ist zu wissen, ob eine unbekannte Eigenschaft erwartet, auf ein gemeinsames Objekt zu verweisen.)
Lindydancer
1

Es wurde eine (nicht sehr effiziente) Problemumgehung gefunden:

(setq test-str-2
      (read (prin1-to-string test-str-1)))
abo-abo
quelle
2
Die Umgehung schlägt fehl, wenn die #Eigenschaften den Charakter enthalten .
Abo-Abo
Meinst du, wenn das Zeichen # Teil des Symbolnamens ist? Oder bedeuten Eigenschaften, die Puffer oder andere nicht druckbare Daten sind? Wenn es das erste ist, sollten Sie einen Fehler melden.
Malabarba
Puffer in Eigenschaften
abo-abo