Diferença de dois buffers sem criar arquivos temporários

9

Eu preciso do diff de dois buffers. Uma maneira seria criar arquivos temporários contendo o conteúdo desses buffers e usar a difffunção No entanto, os buffers contêm informações confidenciais e eu preferiria não ter essas informações em texto não criptografado no disco.

Pensei em usar o ediff, que pode comparar diretamente os buffers, mas o ediff inicia uma sessão interativa e quero usá-lo em um script.

Tmalsburg
fonte
Apenas para esclarecer, você deseja uma função que dê a diferença de dois buffers sem nenhuma interação do usuário?
user2699
@ user2699, precisamente. Contexto: emacs.stackexchange.com/questions/27349/…
tmalsburg 27/09/16
É possível usar pipes nomeados para isso?
tmalsburg 27/09/16
11
Não estou familiarizado com pipes nomeados, mas parece que a melhor solução seria algo além do emacs. Examinando ediff-buffersbrevemente o código fonte , parece salvar buffers em arquivos temporários no disco e, em seguida, chamar o utilitário diff do sistema nesses arquivos, para que não haja nenhuma diferença prática em chamar a diffsi mesmo.
user2699
2
Sob qual sistema operacional? A função diff usa um programa externo e a comunicação com um programa externo depende do sistema operacional. A solução direta seria armazenar os arquivos em um sistema de arquivos na memória; esses são padrão no Linux atualmente, mas podem não existir em outras plataformas.
Gilles 'SO- stop be evil'

Respostas:

3

@tmalsburg, O comando a seguir chama diff em 2 buffers sem a criação de arquivos temporários. Ele usa pipes nomeados, como sugerido acima:

(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. Quando chamado de forma interativa, mostra o diff quando os buffers têm conteúdo diferente.
  2. Quando chamado do Lisp, retorna o código de saída do programa diff; ou seja, 0 se os buffers tiverem o mesmo conteúdo, 1 caso contrário.

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

Testado para o Emacs versão 24.3 em uma máquina executando o Debian GNU / Linux 9.0 (stretch).

  • O código acima parece funcionar quando chamado de Lisp. Infelizmente, na maioria das vezes mostra uma diferença truncada nas chamadas interativas.

  • A versão a seguir usa a biblioteca assíncrona de terceiros ; não trunca as diferenças.

(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
fonte
Obrigado, @tino, é exatamente isso que eu estava procurando!
tmalsburg
0

O AFAIU Emacs usa programas externos para fazer a diferença. Por exemplo ediff-make-diff2-buffer, que compararia dois buffers, chama internamente

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

Onde ediff-diff-programpode representar o GNU / Linux diff. Com o novo FFI, o sistema diffpode estar acessível. Também uma implementação diff do Emacs Lisp pode fazer o trabalho.

Andreas Röhler
fonte
0

Que tal você usar o comando shell para chamar diff, passando um buffer de saída? Ou, shell-command-to-string para obter o diff em uma string

russell
fonte
Não sei ao certo o que você quer dizer com um buffer de saída. Você poderia elaborar um pouco isso? Obrigado.
tmalsburg
A sintaxe do shell-command é (comando do shell COMMAND e opcional OUTPUT-BUFFER ERROR-BUFFER). O segundo argumento opcional é: "" "O segundo argumento opcional OUTPUT-BUFFER, se não for nulo, diz colocar a saída em algum outro buffer. Se OUTPUT-BUFFER for um nome de buffer ou buffer, coloque a saída lá. Se OUTPUT -BUFFER não é um buffer e não é nulo, insira a saída no buffer atual. (Isso não pode ser feito de forma assíncrona.) Em ambos os casos, o buffer é primeiro apagado e a saída é inserida após o ponto (deixando uma marca após). "" "
russell
0

Se você estiver bem com Ediff, isso fará:

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

e chame assim:

(my/diff-buffers "*temp*" "*temp*<2>")
Yasushi Shoji
fonte
Obrigado pela resposta. Existem dois problemas com esta solução. 1. Que eu saiba, o ediff usa diff e, portanto, precisa de arquivos temporários. 2. Sua solução inicia uma sessão interativa, mas eu só quero o diff em um buffer ou string.
tmalsburg
11
Ah, eu li mal sua pergunta. Eu pensei que você não deseja criar arquivos temporários sozinho. emacs.stackexchange.com/questions/19812 está pedindo a versão elisp do diff. Nenhuma menção sobre arquivos temporários, no entanto. Lendo sua pergunta novamente, você precisa ter uma saída diff? ou você quer apenas comparar sua string e conhecer o diff? Um exemplo de chamada pode ajudar.
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 18/07/19