Diferencia de dos buffers sin crear archivos temporales

9

Necesito la diferencia de dos amortiguadores. Una forma sería crear archivos temporales que contengan el contenido de estos búferes y usar la difffunción. Sin embargo, los buffers contienen información confidencial y preferiría no tener esa información en texto claro en el disco.

Pensé en usar ediff, que puede comparar buffers directamente, pero ediff inicia una sesión interactiva y quiero usar esto en un script.

Tmalsburg
fuente
Solo para aclarar, ¿desea una función que proporcione la diferencia de dos buffers sin ninguna interacción del usuario?
usuario2699
@ user2699, precisamente. Contexto: emacs.stackexchange.com/questions/27349/…
tmalsburg
¿Es posible usar tuberías con nombre para esto?
Tmalsburg
1
No estoy familiarizado con las tuberías con nombre, pero parece que la mejor solución sería algo más allá de emacs. Al revisar el código fuente de manera ediff-buffersmuy breve, parece guardar buffers en archivos temporales en el disco, luego llamar a la utilidad diff del sistema en esos archivos, por lo que no habría ninguna diferencia práctica de llamarse a diffsí mismo.
user2699
2
¿Bajo qué sistema operativo? La función diff utiliza un programa externo y la comunicación con un programa externo depende del sistema operativo. La solución directa sería almacenar los archivos en un sistema de archivos en memoria; actualmente son estándar en Linux pero pueden no existir en otras plataformas.
Gilles 'SO- deja de ser malvado'

Respuestas:

3

@tmalsburg, el siguiente comando llama a diff en 2 buffers sin la creación de archivos temporales. Utiliza tuberías con nombre como sugirió anteriormente:

(require 'diff)
(defun diff-buffers-without-temp-files (buffer1 buffer2 &optional switches)
  "Run diff program on BUFFER1 and BUFFER2.
Make the comparison without the creation of temporary files.

When called interactively with a prefix argument, prompt
interactively for diff switches.  Otherwise, the switches
specified in the variable `diff-switches' are passed to the diff command."
  (interactive
   (list (read-buffer "buffer1: " (current-buffer))
         (read-buffer "buffer2: " (current-buffer))
         (diff-switches)))
  (or switches (setq switches diff-switches))
  (unless (listp switches) (setq switches (list switches)))
  (let ((buffers (list buffer1 buffer2))
        (buf (get-buffer-create "*diff-buffers*"))
        fifos res)
    (dotimes (_ 2) (push (make-temp-name "/tmp/pipe") fifos))
    (setq fifos (nreverse fifos))
    (with-current-buffer buf (erase-buffer))
    (unwind-protect
        (progn
          (dotimes (i 2)
            (let ((cmd (format "cat > %s << EOF\n%s\nEOF"
                               (nth i fifos)
                               (with-current-buffer (nth i buffers)
                                 (buffer-string)))))
              (call-process "mkfifo" nil nil nil (nth i fifos))
              (start-process-shell-command (format "p%d" i) nil cmd)))
          (setq res (apply #'call-process diff-command nil buf nil (car fifos) (cadr fifos) switches))
          (if (zerop res)
              (message "Buffers have same content")
            (display-buffer buf)
            (with-current-buffer buf (diff-mode))
            (message "Buffer contents are different"))
          res)
      ;; Clean up.
      (dolist (x fifos)
        (and (file-exists-p x) (delete-file x))))))
  1. Cuando se llama interactivamente, muestra la diferencia cuando los buffers tienen contenido diferente.
  2. Cuando se llama desde Lisp, devuelve el código de salida del programa diff; es decir, 0 si los búferes tienen el mismo contenido, 1 de lo contrario.

    (diff-buffers-without-temp-files (get-buffer "*scratch*") (get-buffer "*scratch*"))
    => 0
    
    
    (diff-buffers-without-temp-files (get-buffer "*scratch*") (get-buffer "*Messages*"))
    => 1
    

Probado para Emacs versión 24.3 en una máquina que ejecuta Debian GNU / Linux 9.0 (stretch).

  • El código anterior parece funcionar cuando se llama desde Lisp. Desafortunadamente, la mayoría de las veces muestra una diferencia truncada en las llamadas interactivas.

  • La siguiente versión usa la biblioteca asíncrona de terceros ; no trunca las diferencias.

(require 'diff)
(require 'async)
(defun diff-buffers-without-temp-files (buffer1 buffer2 &optional switches)
  "Run diff program on BUFFER1 and BUFFER2.
Make the comparison without the creation of temporary files.

When called interactively with a prefix argument, prompt
interactively for diff switches.  Otherwise, the switches
specified in the variable `diff-switches' are passed to the diff command."
  (interactive
   (list (read-buffer "buffer1: " (current-buffer))
         (read-buffer "buffer2: " (current-buffer))
         (diff-switches)))
  (or switches (setq switches diff-switches))
  (unless (listp switches) (setq switches (list switches)))
  (let ((buffers (list buffer1 buffer2))
        (buf (get-buffer-create "*diff-buffers*"))
        fifos res)
    (dotimes (_ 2) (push (make-temp-name "/tmp/pipe") fifos))
    (setq fifos (nreverse fifos))
    (with-current-buffer buf (erase-buffer))
    (unwind-protect
        (progn
          (dotimes (i 2)
            (let ((cmd (format "cat > %s" (nth i fifos))))
              (call-process "mkfifo" nil nil nil (nth i fifos))
              (async-start
               `(lambda ()
                  (with-temp-buffer
                    (insert ,(with-current-buffer (nth i buffers) (buffer-string)))
                    (call-process-region
                     1 (point-max) shell-file-name nil nil nil
                     shell-command-switch ,cmd))))))
          (setq res (apply #'call-process diff-command nil buf nil (car fifos) (cadr fifos) switches))
          (if (zerop res)
              (message "Buffers have same content")
            (display-buffer buf)
            (with-current-buffer buf (diff-mode))
            (message "Buffer contents are different"))
          res)
      ;; Clean up.
      (dolist (x fifos)
        (and (file-exists-p x) (delete-file x))))))
Tino
fuente
¡Gracias, @tino, esto es exactamente lo que estaba buscando!
Tmalsburg
0

AFAIU Emacs toma programas externos para hacer la diferencia. Por ejemplo ediff-make-diff2-buffer, que compararía dos buffers, internamente llama

  (ediff-exec-process ediff-diff-program
                 diff-buffer
                 'synchronize
                 ediff-actual-diff-options file1 file2)

Donde ediff-diff-programpodría representar el GNU / Linux diff. Con la nueva FFI, el sistema diffpodría ser accesible. También una implementación diferencial de Emacs Lisp podría hacer el trabajo.

Andreas Röhler
fuente
0

¿Qué tal si usas el comando shell para llamar a diff, pasándole un buffer de salida? O, shell-command-to-string para obtener el diff en una cadena

Russell
fuente
No estoy seguro de entender lo que quieres decir al pasarle un búfer de salida. ¿Podría por favor elaborar esto un poco? Gracias.
Tmalsburg 01 de
La sintaxis de shell-command es (shell-command COMMAND y opcional OUTPUT-BUFFER ERROR-BUFFER). El segundo argumento opcional es: "" "El segundo argumento opcional OUTPUT-BUFFER, si no es nulo, dice poner la salida en algún otro búfer. Si OUTPUT-BUFFER es un búfer o nombre de búfer, ponga la salida allí. Si OUTPUT -BUFFER no es un búfer y no es nulo, inserte la salida en el búfer actual. (Esto no se puede hacer de forma asíncrona). En cualquier caso, el búfer se borra primero y la salida se inserta después del punto (dejando la marca después de él). "" "
russell
0

Si está de acuerdo con Ediff, esto hará:

(defun my/diff-buffers (buffer-A buffer-B)
  "Run Ediff on a pair of buffers, BUFFER-A and BUFFER-B."
  (ediff-buffers-internal buffer-A buffer-B nil nil 'ediff-buffers))

y llámalo así:

(my/diff-buffers "*temp*" "*temp*<2>")
Yasushi Shoji
fuente
Gracias por la respuesta. Hay dos problemas con esta solución. 1. Que yo sepa, ediff usa diff y por lo tanto necesita archivos temporales. 2. Su solución inicia una sesión interactiva pero solo quiero el diff en un búfer o cadena.
Tmalsburg
1
Ah, leí mal tu pregunta. Pensé que no querías crear archivos temporales por ti mismo. emacs.stackexchange.com/questions/19812 está pidiendo la versión elisp de diff. Sin embargo, no se mencionan los archivos temporales. Al leer su pregunta nuevamente, ¿tiene que tener salida diff? o simplemente quieres comparar tu cadena y conocer la diferencia? Un ejemplo de llamada podría ayudar.
Yasushi Shoji
(defun my/diff-buffers (buffer-A buffer-B) "Run Ediff on a pair of buffers, BUFFER-A and BUFFER-B." (interactive (list (read-buffer "buffer1: " (current-buffer)) (read-buffer "buffer2: " (current-buffer)))) (ediff-buffers-internal buffer-A buffer-B nil nil 'ediff-buffers))
HappyFace