Une méthode plus rapide pour obtenir `line-number-at-pos` dans les grands tampons

19

La fonction line-number-at-pos(lorsqu'elle est répétée environ 50 fois) provoque un ralentissement notable dans les tampons semi-volumineux - par exemple, 50 000 lignes - lorsque le point est proche de la fin du tampon. Par ralentissement, je veux dire un total combiné d'environ 1,35 seconde.

Au lieu d'utiliser une fonction à 100% elisppour compter les lignes et atteindre le haut du tampon, je serais intéressé par une méthode hybride qui exploite les capacités C intégrées responsables du numéro de ligne apparaissant sur la ligne de mode. Le numéro de ligne qui apparaît sur la ligne de mode se produit à la vitesse de la lumière, quelle que soit la taille du tampon.


Voici une fonction de 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))
liste des lois
la source

Réponses:

17

Essayer

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

Vous pouvez extraire d'autres informations en utilisant % -Constructs décrit dans le manuel Emacs Lisp.

Caveat:

En plus des limitations signalées par wasamasa et Stefan (voir les commentaires ci-dessous), cela ne fonctionne pas pour les tampons qui ne sont pas affichés.

Essaye ça:

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

et comparer à

(with-temp-buffer
  (dotimes (i 10000)
    (insert (format "%d\n" i)))
  (line-number-at-pos))
Constantine
la source
Oui, cela l'a réduit de 1,35 seconde à 0,003559! Merci beaucoup - grandement apprécié! :)
Lawlist
6
Soyez conscient que cette méthode vous donnera "??" pour les lignes dépassant line-number-display-limit-widthce qui est fixé à une valeur de 200 par défaut comme je l'ai découvert ici .
wasamasa
3
IIRC le résultat peut également être peu fiable s'il y a eu des modifications dans le tampon depuis le dernier réaffichage.
Stefan
Je pense qu'il serait nécessaire de modifier les tests dans la réponse de telle sorte que la deuxième lettre isoit remplacée par (string-to-number (format-mode-line "%l"))pour le premier test, et la deuxième lettre isoit remplacée par (line-number-at-pos)pour le deuxième test.
lawlist
5

nlinum.el utilise les éléments suivants:

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

avec la configuration supplémentaire suivante dans la fonction mode:

(add-hook 'after-change-functions #'nlinum--after-change nil t)
Stefan
la source
1
Ah ... je pensais à ta bibliothèque plus tôt ce matin. Le line-number-at-pospourrait être remplacé par la réponse de Constantine, et cela accélérerait encore plus votre bibliothèque qu'elle ne l'est déjà - en particulier dans les grands tampons. count-linesdevrait également être corrigé en utilisant la méthode de Constantine. Je pensais même à envoyer une soumission de suggestions à la hotline report-emacs-bug pour corriger ces fonctions.
lawlist