Глубоко скопировать строку в Elisp?

9

У меня есть специальная строка. Я хочу сделать его глубокую копию, чтобы добавить больше свойств, сохраняя при этом свойства в исходной строке. Как я могу это сделать (легко)?

пример

Оцените один за другим:

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

И результат:

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
Я бы сообщил об этом как об ошибке в add-face-text-property. Он не должен деструктивно изменять список, поскольку он терпит неудачу, когда на этот список ссылаются другие.
Lindydancer
1
Хорошо, сообщили об ошибке на debbugs.gnu.org/cgi/bugreport.cgi?bug=20153
abo-abo
Спасибо за сообщение об ошибке. Жаль, что никто еще не ответил на это. Было бы хорошо, чтобы исправить эту функцию полезности (кодируется в C).
Дрю

Ответы:

7

Вы можете использовать функцию font-lock-append-text-propertyдля добавления свойства текста. Это не изменяет значение разрушительно.

Например:

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

Здесь, в test-str-1, сохранил свое первоначальное значение.

Lindydancer
источник
4

Я обнаружил, что вы можете сделать это, перебирая свойства текста, копируя данные базовых свойств и перезаписывая существующие свойства новыми копиями.

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

В моих тестах это было примерно на 20% быстрее, чем ваше readрешение. Я также написал версию, в которой не использовался временный буфер, и изменил свойства строки, которая была меньше кода, но была медленнее.

Глядя на код C, он копирует списки свойств с помощью copy_sequence, который перестраивает структуру списка, но не копирует элементы по значению, поэтому свойства, такие как face в вашем примере, которые имеют значение списка, копируются по ссылке и изменяются. Ошибка или нет, я не знаю

Джордон Биондо
источник
2

Вы можете использовать (concat the-original-string).

Например:

(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
источник
1
Не работает, я добавлю пример.
abo-abo
1
Хитрость заключается в том, чтобы иметь вложенный список в свойствах, как я. Тогда concatне работает.
abo-abo
@ або-або. Хорошо, теперь я вижу. Я не заметил этого в вашем добавленном примере. В этом случае у меня нет ответа, но я думаю, что существует реальная потребность в такой функции. (Одна потенциальная проблема заключается в том, что невозможно узнать, может ли неизвестное свойство ожидать ссылки на какой-либо общий объект.)
Lindydancer
1

Нашел (не очень эффективный) обходной путь:

(setq test-str-2
      (read (prin1-to-string test-str-1)))
Або-або
источник
2
Обходной путь не удается, если в свойствах есть #персонаж.
abo-abo
Вы имеете в виду, если символ # является частью имени символа? Или имеют в виду свойства, которые являются буферами или другими непечатными данными? Если это первое, вы должны сообщить об ошибке.
Малабарба
буферы в свойствах
abo-abo