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.