Como destacar linhas duplicadas?

8

Aqui está o que eu tenho:

(defun my-show-duplicate-lines ()
  (interactive)
  (highlight-lines-matching-regexp
   (concat "^"
           (regexp-quote
            (substring-no-properties
             (thing-at-point 'line) 0 -1))
           "$")
   font-lock-warning-face))

Minha intenção é adicionar isso para post-command-hookencontrar linhas nas quais estou duplicando a lógica, mas quando executo o comando pela segunda vez, é ineficaz (e o antigo bloqueio de fonte ainda está em vigor).

O segundo problema é causado pelo bloqueio de fonte não se atualizar. Eu tentei adicionar um (font-lock-mode -1) (font-lock-mode 1)à definição, mas era ineficaz.

Não tenho idéia do motivo pelo qual o comando seria bom apenas para uma corrida.

Sean Allred
fonte
Tente envolver o highlight-lines-matching-regexpinterior (let ((hi-lock-mode -1)) .. ). Eu fiz isso para resolver o mesmo problema: github.com/kaushalmodi/.emacs.d/blob/...
Kaushal Modi
unhighlight-regexptambém pode ser usado. Independentemente, esse recurso provavelmente é melhor implementado usando uma função de correspondência de bloqueio de fonte que varre o buffer em busca de linhas duplicadas e aplica realce a elas. Isso lidaria com a remoção de destaque automaticamente quando não houver linhas duplicadas.
Jordon Biondo
@kaushalmodi não tive essa sorte :( graças embora
Sean Allred
@JordonBiondo Eu pensei sobre isso, mas highlight-lines-matching-regexpdeve ser aplicável a este caso - é quase um entrave . (Embora eu também pensei sobre o uso de sobreposições, mas isso é um conceito que eu sou menos familiarizados com.)
Sean Allred
Você pode copiar o conteúdo do buffer para outro buffer, executar delete-duplicate-linese depois diferenciar dois buffers.
wvxvw

Respostas:

6
  1. Dê uma olhada font-lock-keywordsdepois de ter chamado sua função. Você verá que ele apenas possui o regexp para a primeira linha como o regexp para tipificar. Tudo o que você fez foi pegar uma determinada linha e colocar um regexp para combiná-la font-lock-keywords- para que apenas os dups dessa linha sejam destacados. IOW, o regexp para essa primeira linha é codificado font-lock-keywords.

  2. Em vez disso, você pode usar um FUNCTIONin font-lock-keywords. Mas eu procuraria no buffer por dups de cada linha, por sua vez, e não me incomodaria font-lock-keywords.

Aqui está uma solução rápida. Ele usa a função hlt-highlight-regionda biblioteca Highlight ( highlight.el), mas você pode usar outra coisa, se quiser.

(defun highlight-line-dups ()
  (interactive)
  (let ((count  0)
        line-re)
    (save-excursion
      (goto-char (point-min))
      (while (not (eobp))
        (setq count    0
              line-re  (concat "^" (regexp-quote (buffer-substring-no-properties
                                                  (line-beginning-position)
                                                  (line-end-position)))
                               "$"))
        (save-excursion
          (goto-char (point-min))
          (while (not (eobp))
            (if (not (re-search-forward line-re nil t))
                (goto-char (point-max))
              (setq count  (1+ count))
              (unless (< count 2)
                (hlt-highlight-region (line-beginning-position) (line-end-position)
                                      'font-lock-warning-face)
                (forward-line 1)))))
        (forward-line 1)))))

E aqui está uma versão que funciona (a) na região ativa ou (b) no buffer completo se a região não estiver ativa:

(defun highlight-line-dups-region (&optional start end face msgp)
  (interactive `(,@(hlt-region-or-buffer-limits) nil t))
  (let ((count  0)
        line-re)
    (save-excursion
      (goto-char start)
      (while (< (point) end)
        (setq count    0
              line-re  (concat "^" (regexp-quote (buffer-substring-no-properties
                                                  (line-beginning-position)
                                                  (line-end-position)))
                               "$"))
        (save-excursion
          (goto-char start)
          (while (< (point) end)
            (if (not (re-search-forward line-re nil t))
                (goto-char end)
              (setq count  (1+ count))
              (unless (< count 2)
                (hlt-highlight-region
                 (line-beginning-position) (line-end-position)
                 face)
                (forward-line 1)))))
        (forward-line 1)))))

E se você quiser uma face diferente para cada conjunto de cópias de segurança , basta vincular uma variável faceno let, e setqao (hlt-next-face)lado de onde line-reestá definido, e substitua font-lock-warning-facepor face. A opção hlt-auto-face-backgroundscontrola as faces usadas.

(defun hlt-highlight-line-dups-region (&optional start end msgp)
  (interactive `(,@(hlt-region-or-buffer-limits) t))
  (let ((hlt-auto-faces-flag  t)
        count line line-re ignore-re)
    (save-excursion
      (goto-char start)
      (while (< (point) end)
        (setq count    0
              line     (buffer-substring-no-properties (line-beginning-position)
                                                       (line-end-position))
              ignore   (and (not (string= "" line))  "[ \t]*")
              line-re  (concat "^" ignore (regexp-quote line) ignore "$"))
        (save-excursion
          (goto-char start)
          (while (< (point) end)
            (if (not (re-search-forward line-re end t))
                (goto-char end)
              (setq count  (1+ count))
              (unless (< count 2)
                (hlt-highlight-region (line-beginning-position) (line-end-position))
                (forward-line 1)))))
        (forward-line 1)))))
Desenhou
fonte
Na verdade, eu estava apenas trabalhando em algo que parece quase o mesmo, função por função! Uma coisa que eu sugeriria é remover o espaço em branco à esquerda / à direita do texto da linha e adicionar algo como [\ t] * no início e no final da regexp para que as linhas em diferentes níveis de recuo ainda correspondam.
Jordon Biondo
@JordonBiondo: Mas não foi isso que o OP pediu. Tudo é possível, mas peguei minha sugestão da pergunta e da tentativa de solução: ele aparentemente quer mesmo corresponder exatamente ao texto da linha e começar com bol, ou seja, sem desconsiderar o recuo ou o espaço em branco à direita. Mas sim, muitas variantes são possíveis. Não sei quão útil é realmente algo assim. Eu acho que depende do que você quer fazer com isso.
Tirou
Bem, meu caso de uso é reconhecer onde a lógica está sendo duplicada para que eu possa tentar otimizar :) Estou desenhando um algoritmo e usando uma sintaxe formal, para que duplicatas exatas sejam mais do que possíveis.
Sean Allred
Não sei o que você quer dizer, Sean. Mas se você deseja ignorar os espaços em branco iniciais e finais, como sugeriu @JordonBiondo, faça o que ele sugeriu: adicione um prefixo de espaço em branco possível e sufixo ao regexp.
Tirou
Eu tentei usar sua última função, mas ao compilar a definição de função que recebo setq: Symbol's value as variable is void: hlt-highlight-line-dups-ignore-regexp. Como essa variável é definida?
Patrick
1

Que tal usar sobreposição em vez de bloqueio de fonte?

;; https://github.com/ShingoFukuyama/ov.el
(require 'ov)

(defun my-highlight-duplicate-lines-in-region ()
  (interactive)
  (if mark-active
      (let* (($beg (region-beginning))
             ($end (region-end))
             ($st (buffer-substring-no-properties
                   $beg $end))
             ($lines)
             $dup)
        (deactivate-mark t)
        (save-excursion
          (goto-char $beg)
          (while (< (point) $end)
            (let* (($b (point))
                   ($e (point-at-eol))
                   ($c (buffer-substring-no-properties $b $e))
                   ($a (assoc $c $lines)))
              (when (not (eq $b $e))
                (if $a
                    (progn
                      (setq $dup (cons $b $dup))
                      (setq $dup (cons (cdr $a) $dup)))
                  (setq $lines
                        (cons (cons $c $b) $lines)))))
            (forward-line 1))
          (mapc (lambda ($p)
                  (ov-set (ov-line $p) 'face '(:foreground "red")))
                (sort (delete-dups $dup) '<))))))

Crie região e, em seguida, M-x my-highlight-duplicate-lines-in-region você pode limpar todas as sobreposiçõesM-x ov-clear

Shingo Fukuyama
fonte
0

Isso é um pouco superficial, mas com algum esforço (consulte C-h fediff-buffersRETas informações sobre o HOOKargumento) você pode fazer com que ele seja exibido melhor / faça uma limpeza melhor ao sair do modo diff:

(defun my/show-duplicate-lines (beg end)
  (interactive "r")
  (unless (region-active-p)
    (setf beg (point-min)
          end (point-max)))
  (let ((copy (buffer-substring beg end))
        (original (current-buffer))
        (dupes-buffer (get-buffer-create (format "%s[dupes]" (buffer-name)))))
    (with-current-buffer dupes-buffer
      (erase-buffer)
      (insert copy)
      (delete-duplicate-lines (point-min) (point-max))
      (ediff-buffers original dupes-buffer))))
wvxvw
fonte
0

Melhoria na resposta acima por Shingo Fukuyama.

Esta versão verifica se há linhas duplicadas na região ativa, mas se não houver nenhuma, pesquisa o buffer inteiro.

(require 'ov)
(defun highlight-duplicate-lines-in-region-or-buffer ()
(interactive)

  (let* (
    ($beg (if mark-active (region-beginning) (point-min)))
    ($end (if mark-active (region-end) (point-max)))
    ($st (buffer-substring-no-properties $beg $end))
    ($lines)
    ($dup))
  (deactivate-mark t)
  (save-excursion
    (goto-char $beg)
    (while (< (point) $end)
      (let* (($b (point))
         ($e (point-at-eol))
         ($c (buffer-substring-no-properties $b $e))
         ($a (assoc $c $lines)))
    (when (not (eq $b $e))
      (if $a
          (progn
        (setq $dup (cons $b $dup))
        (setq $dup (cons (cdr $a) $dup)))
        (setq $lines
          (cons (cons $c $b) $lines)))))
      (forward-line 1))
    (mapc (lambda ($p)
        (ov-set (ov-line $p) 'face '(:foreground "red")))
      (sort (delete-dups $dup) '<)))))
Sawan
fonte