Um método mais rápido para obter `número de linha em posição 'em buffers grandes

19

A função line-number-at-pos(quando repetida cerca de 50 vezes) está causando um abrandamento perceptível nos buffers semi-grandes - por exemplo, 50.000 linhas - quando o ponto está próximo ao final do buffer. Por desaceleração, quero dizer um total combinado de cerca de 1,35 segundos.

Em vez de usar uma função 100% elisppara contar linhas e ir para o topo do buffer, eu estaria interessado em um método híbrido que explora as habilidades C integradas responsáveis ​​pelo número da linha que aparece na linha de modo. O número da linha que aparece na linha de modo ocorre na velocidade da luz, independentemente do tamanho do buffer.


Aqui está uma função de teste:

(defmacro measure-time (&rest body)
"Measure the time it takes to evaluate BODY.
http://lists.gnu.org/archive/html/help-gnu-emacs/2008-06/msg00087.html"
  `(let ((time (current-time)))
     ,@body
     (message "%.06f" (float-time (time-since time)))))

(measure-time
  (let* (
      line-numbers
      (window-start (window-start))
      (window-end (window-end)))
    (save-excursion
      (goto-char window-end)
      (while
        (re-search-backward "\n" window-start t)
        (push (line-number-at-pos) line-numbers)))
    line-numbers))
lista de leis
fonte

Respostas:

17

Experimentar

(string-to-number (format-mode-line "%l"))

Você pode extrair outras informações usando % -Constructs descritos no Emacs Lisp Manual.

Embargo:

Além das limitações apontadas por wasamasa e Stefan (veja os comentários abaixo), isso não funciona para buffers que não são exibidos.

Tente o seguinte:

(with-temp-buffer
  (dotimes (i 10000)
    (insert (format "%d\n" i)))
  (string-to-number (format-mode-line "%l")))

e compare com

(with-temp-buffer
  (dotimes (i 10000)
    (insert (format "%d\n" i)))
  (line-number-at-pos))
Constantine
fonte
Sim, isso reduziu de 1,35 segundos para 0,003559! Muito obrigado - muito apreciado! :)
lawlist
6
Esteja ciente de que este método lhe dará "??" para linhas excedendo, line-number-display-limit-widthque é definido como um valor de 200 por padrão, como descobri aqui .
wasamasa
3
IIRC, o resultado também poderá não ser confiável se houver modificações no buffer desde o último reexibimento.
Stefan
Acredito que seria necessário modificar os testes na resposta para que a segunda letra iseja substituída (string-to-number (format-mode-line "%l"))pelo primeiro teste e a segunda letra iseja substituída (line-number-at-pos)pelo segundo teste.
lawlist
5

O nlinum.el usa o seguinte:

(defvar nlinum--line-number-cache nil)
(make-variable-buffer-local 'nlinum--line-number-cache)

;; We could try and avoid flushing the cache at every change, e.g. with:
;;   (defun nlinum--before-change (start _end)
;;     (if (and nlinum--line-number-cache
;;              (< start (car nlinum--line-number-cache)))
;;         (save-excursion (goto-char start) (nlinum--line-number-at-pos))))
;; But it's far from clear that it's worth the trouble.  The current simplistic
;; approach seems to be good enough in practice.

(defun nlinum--after-change (&rest _args)
  (setq nlinum--line-number-cache nil))

(defun nlinum--line-number-at-pos ()
  "Like `line-number-at-pos' but sped up with a cache."
  ;; (assert (bolp))
  (let ((pos
         (if (and nlinum--line-number-cache
                  (> (- (point) (point-min))
                     (abs (- (point) (car nlinum--line-number-cache)))))
             (funcall (if (> (point) (car nlinum--line-number-cache))
                          #'+ #'-)
                      (cdr nlinum--line-number-cache)
                      (count-lines (point) (car nlinum--line-number-cache)))
           (line-number-at-pos))))
    ;;(assert (= pos (line-number-at-pos)))
    (setq nlinum--line-number-cache (cons (point) pos))
    pos))

com a seguinte configuração extra na função mode:

(add-hook 'after-change-functions #'nlinum--after-change nil t)
Stefan
fonte
1
Ah ... eu estava pensando na sua biblioteca hoje de manhã. O line-number-at-pospoderia ser substituído pela resposta de Constantine, o que aceleraria sua biblioteca ainda mais do que já é - especialmente em buffers grandes. count-linestambém deve ser corrigido usando o método de Constantine. Eu estava pensando em enviar um envio de caixa de sugestões para a linha direta report-emacs-bug para corrigir essas funções.
lawlist