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

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

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)

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.