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

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

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

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.