Como evitar a desaceleração quando um processo inferior gera longas filas?

14

Eu uso o Emacs com o Geiser para invadir algum código do esquema. Como eu estou brincando no REPL, às vezes avalio expressões que resultam em muita saída, geralmente todas em uma linha.

Por exemplo, eu apenas brinquei com SRFI-41 (streams) e criei um fluxo de caracteres a partir de um arquivo grande; forcei o fluxo e Geiser vomitou todo o conteúdo do arquivo como um fluxo de caracteres no meu buffer. Quase imediatamente, o Emacs parou, à medida que mais e mais caracteres foram anexados à linha de saída, e não importa quanto tempo continuei pressionando C-gou C-c C-cnão consegui fazer o Emacs (ou Geiser) parar.

Isso interrompeu toda a sessão do Emacs, já que o Emacs agora ignora completamente minha opinião, pensando que ele precisa dar prioridade à impressão desse enorme fluxo de caracteres em uma linha em um buffer Geiser REPL que não responde.

Existe algo que eu possa fazer para proteger minha sessão do Emacs da minha curiosidade destrutiva? (Por que o Emacs fica tão lento ao exibir linhas muito longas?) Posso definir um limite para linhas longas e dizer ao Emacs que não há problema em simplesmente não tentar exibir linhas muito longas?

rekado
fonte
2
Bem, minha pergunta não é sobre a exibição de longas filas em si; Quero saber como evitar esse tipo de coisa em primeiro lugar (o Emacs lê a linha de um processo inferencial, não é lido de um arquivo que eu poderia corrigir); e é sobre como posso evitar perder minha sessão do Emacs devido à dedicação do Emacs a um único buffer dinâmico.
rekado
"Bem, minha pergunta não é sobre a exibição de linhas longas". Então talvez você deva mudar seu título. Talvez você queira filtrar a saída inferior do processo e adicionar nova linha após uma certa quantidade de caracteres?
Nanny
Realmente, porém, isso tem pouco a ver com longas filas. yesem um, ansi-termpor exemplo, tem um efeito semelhante (mas não tão horrível). Realmente, é apenas o volume de texto que dá uma pausa no emacs.
PythonNut
A inserção de texto em um buffer é bastante rápida, são as operações de reexibição que a tornam mais lenta do que realmente é. Para ser sincero, a execução yesem um emulador de terminal VTE atinge o máximo de todos os núcleos da minha CPU, portanto, não o usaria como exemplo.
wasamasa

Respostas:

12

Como já respondido nos comentários, o Emacs se torna muito lento em sua reexibição de longas filas é um problema bem conhecido . Consertá-lo seria muito bom, mas precisa de muita reflexão para ser realizado corretamente. Tenho uma ideia de como isso pode ser realizado com base na seção 6.3 deste documento (basicamente, armazene informações da linha visual no buffer atual e atualize-a na inserção de espaço em branco, propriedades de exibição, alterações de janela etc.) e use essas informações em o código de reexibição para evitar a varredura o tempo todo), mas não estou familiarizado o suficiente com os C internos para removê-lo.

Existem soluções alternativas. Os mais óbvios seriam ajustar os parâmetros relacionados à exibição (como ativar o truncamento de linha visual na instância gráfica do Emacs, usar um Emacs não gráfico para fazer isso automaticamente, desativar os recursos do Bidi etc.) e pré-processar o conteúdo do arquivo que você ' re-lendo. Um menos óbvio é o pós-processamento automático dos arquivos, seja truncando suas linhas ou adicionando propriedades de texto que tornam as linhas mais curtas do que realmente são. Para transformar isso em uma resposta mais interessante, apresentarei um truque bastante feio da opção anterior que funcionará apenas nos comintmodos derivados:

(defun my-comint-shorten-long-lines (text)
  (let* ((regexp "^\\(.\\{80\\}\\).*?$")
         (shortened-text (replace-regexp-in-string regexp "\\1" text)))
    (if (string= shortened-text text)
        text
      (propertize shortened-text 'help-echo text))))

(add-hook 'comint-preoutput-filter-functions 'my-comint-shorten-long-lines)

Isso define my-comint-shorten-long-linesuma função que utiliza uma sequência possivelmente composta de muitas linhas e usa o poder das expressões regulares para substituir qualquer linha com um comprimento de 80 caracteres ou mais por uma versão reduzida que exibe o texto original ao passar o mouse sobre ela. Quando usado como gancho, comint-preoutput-filter-functionsele filtra toda a comintsaída antes de ser exibida.

No entanto, esta versão do hack tem uma fraqueza bastante séria. Nos modos em que a fonte básica está em andamento (como, por exemplo M-x ielm), ela cortará alegremente as linhas que fazem parte de uma cadeia de caracteres e, dessa forma, tipificará tudo até a próxima citação como string! Não é isso que queremos e pode ser corrigido com um pouco mais de domínio sobre expressões regulares (mas presumivelmente entrará em um REPL para uma linguagem como Python). Enquanto estamos nisso, vamos destacar a saída reduzida também:

(defun my-comint-shorten-long-lines (text)
  (let* ((regexp "^\\(.\\{80\\}\\).*?\\(\"?\\)$")
         (shortened-text (replace-regexp-in-string regexp "\\1\\2" text)))
    (if (string= shortened-text text)
        text
      (propertize shortened-text 'font-lock-face 'shadow 'help-echo text))))

(add-hook 'comint-preoutput-filter-functions 'my-comint-shorten-long-lines)

Isso é um pouco melhor, mas ainda feio. Pairar sobre a saída de algo como find /in M-x shellnão é atraente (idealmente, queremos exibir apenas a linha não abreviada, nem todas as saídas), a detecção de strings é rudimentar na melhor das hipóteses e o truncamento pode ser indicado melhor com elipses em vez de especificar tudo. Além disso, não é garantido que o texto recebido não seja transformado em lotes. Tudo isso grita por executar a etapa de processamento em um buffer temporário, mas será deixado para o leitor como exercício (ou o autor como possível postagem no blog).

wasamasa
fonte
4

Como isso aconteceu com o Python da mesma forma, a solução em python-mode.el, https://launchpad.net/python-mode , é conectar-se ao processo diretamente, não através do modo de comint.

Confia em start-processe process-send-string

Por exemplo, consulte funções py--start-fast-processepy--fast-send-string-intern

Andreas Röhler
fonte