Un método más rápido para obtener `line-number-at-pos` en grandes buffers

19

La función line-number-at-pos(cuando se repite unas 50 veces) está causando una desaceleración notable en los búferes semi-grandes, por ejemplo, 50,000 líneas, cuando el punto está cerca del final del búfer. Por desaceleración, me refiero a un total combinado de aproximadamente 1.35 segundos.

En lugar de usar una elispfunción 100% para contar líneas y pasar a la parte superior del búfer, me interesaría un método híbrido que aproveche las habilidades C integradas responsables del número de línea que aparece en la línea de modo. El número de línea que aparece en la línea de modo se produce a la velocidad de la luz, independientemente del tamaño del búfer.


Aquí hay una función de prueba:

(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 leyes
fuente

Respuestas:

17

Tratar

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

Puede extraer otra información utilizando % -Constructs descritos en el Manual Emacs Lisp.

Consideración:

Además de las limitaciones señaladas por wasamasa y Stefan (ver comentarios a continuación), esto no funciona para los buffers que no se muestran.

Prueba esto:

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

y comparar con

(with-temp-buffer
  (dotimes (i 10000)
    (insert (format "%d\n" i)))
  (line-number-at-pos))
Constantina
fuente
¡Sí, eso lo redujo de 1.35 segundos a 0.003559! Muchas gracias, muy apreciado! :)
abogados
66
Tenga en cuenta que este método le dará "??" para las líneas line-number-display-limit-widthque exceden el valor predeterminado de 200, como descubrí aquí .
wasamasa
3
IIRC el resultado también puede ser poco confiable si ha habido modificaciones en el búfer desde la última pantalla.
Stefan
Creo que sería necesario modificar las pruebas en la respuesta de modo que la segunda letra ise reemplace (string-to-number (format-mode-line "%l"))por la primera prueba, y la segunda letra ise reemplace (line-number-at-pos)por la segunda prueba.
ley
5

nlinum.el usa lo siguiente:

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

con la siguiente configuración adicional en la función de modo:

(add-hook 'after-change-functions #'nlinum--after-change nil t)
Stefan
fuente
1
Ah ... Estaba pensando en tu biblioteca más temprano esta mañana. El line-number-at-pospodría sustituirse con la respuesta por Constantino, y que podría acelerar su biblioteca aún más de lo que ya su - especialmente en las grandes memorias intermedias. count-linestambién debe arreglarse usando el método de Constantine. Incluso estaba pensando en enviar una presentación de buzón de sugerencias a la línea directa report-emacs-bug para corregir esas funciones.
abogados el