Un metodo più veloce per ottenere `line-number-at-pos` in buffer di grandi dimensioni


19

La funzione line-number-at-pos(se ripetuta circa 50 volte) sta causando un notevole rallentamento nei buffer semi-grandi - ad esempio, 50.000 linee - quando il punto è vicino alla fine del buffer. Per rallentamento, intendo un totale combinato di circa 1,35 secondi.

Invece di usare una funzionalità al 100% elispper contare le righe e andare in cima al buffer, sarei interessato a un metodo ibrido che attinge alle abilità C integrate responsabili del numero di riga che appare sulla linea di modalità. Il numero di riga che appare sulla riga di modalità viene visualizzato alla velocità della luce, indipendentemente dalle dimensioni del buffer.


Ecco una funzione di test:

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

Risposte:


17

Provare

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

Puoi estrarre altre informazioni usando % -Constructs descritte nel Manuale Emacs Lisp.

Avvertimento:

Oltre alle limitazioni evidenziate da wasamasa e Stefan (vedere i commenti di seguito), ciò non funziona per i buffer che non vengono visualizzati.

Prova questo:

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

e confronta con

(with-temp-buffer
  (dotimes (i 10000)
    (insert (format "%d\n" i)))
  (line-number-at-pos))

Sì, ciò lo ha ridotto da 1,35 secondi a 0,003559! Grazie mille - molto apprezzato! :)
elenco delle leggi il

6
Tieni presente che questo metodo ti darà "??" per le linee line-number-display-limit-widthche superano il valore impostato su 200 per impostazione predefinita, come ho scoperto qui .
Wasamasa,

3
IIRC il risultato potrebbe anche essere inaffidabile se ci sono state modifiche nel buffer dall'ultima visualizzazione.
Stefan,

Ritengo che sarebbe necessario modificare i test nella risposta in modo tale che la seconda lettera isia sostituita con (string-to-number (format-mode-line "%l"))la prima e la seconda lettera isia sostituita con (line-number-at-pos)la seconda.
elenco delle leggi

5

nlinum.el utilizza quanto segue:

(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 seguente configurazione aggiuntiva nella funzione mode:

(add-hook 'after-change-functions #'nlinum--after-change nil t)

1
Ah ... stavo solo pensando alla tua biblioteca stamattina. Il line-number-at-pospotrebbe essere sostituito con la risposta di Costantino, e ciò accelererebbe la tua libreria anche più di quanto non lo sia già, specialmente nei buffer di grandi dimensioni. count-linesdovrebbe anche essere risolto usando il metodo di Costantino. Stavo persino pensando di inviare una proposta di suggerimento alla hotline di report-emacs-bug per correggere quelle funzioni.
elenco delle leggi del
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.