Szybsza metoda uzyskania `line-number-at-pos` w dużych buforach

19

Funkcja line-number-at-pos(powtórzona około 50 razy) powoduje zauważalne spowolnienie w buforach pół-dużych - np. 50 000 linii - gdy punkt znajduje się w pobliżu końca bufora. Przez spowolnienie mam na myśli łącznie około 1,35 sekundy.

Zamiast używać 100% elispfuncitonu do zliczania linii i przejścia do górnej części bufora, byłbym zainteresowany hybrydową metodą, która wykorzystuje wbudowane zdolności C odpowiedzialne za numer linii pojawiający się na linii trybu. Numer linii, który pojawia się na linii trybu, występuje z prędkością światła, niezależnie od wielkości bufora.


Oto funkcja testowa:

(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))
lista prawnicza
źródło

Odpowiedzi:

17

Próbować

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

Możesz wyodrębnić inne informacje za pomocą % -Constructs opisanego w Podręczniku Emacsa Lispa.

Zastrzeżenie:

Oprócz ograniczeń wskazanych przez wasamasa i Stefana (patrz komentarze poniżej) nie działa to dla buforów, które nie są wyświetlane.

Spróbuj tego:

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

i porównaj z

(with-temp-buffer
  (dotimes (i 10000)
    (insert (format "%d\n" i)))
  (line-number-at-pos))
Konstantyn
źródło
Tak, to zmniejszyło go z 1,35 sekundy do 0,003559! Dziękuję bardzo - bardzo mile widziane! :)
prawnik
6
Pamiętaj, że ta metoda da ci „??” dla linii przekraczających line-number-display-limit-widthktóra jest ustawiona na wartość 200 domyślnie, jak się tutaj dowiedziałem .
wasamasa,
3
IIRC wynik może być również niewiarygodny, jeśli w buforze nastąpiły modyfikacje od czasu ostatniego ponownego wyświetlenia.
Stefan
Uważam, że konieczne byłoby zmodyfikowanie testów w odpowiedzi, tak aby druga litera izostała zastąpiona (string-to-number (format-mode-line "%l"))przez pierwszy test, a druga litera izostała zastąpiona (line-number-at-pos)przez drugi test.
prawnik
5

nlinum.el wykorzystuje:

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

z następującą dodatkową konfiguracją w funkcji trybu:

(add-hook 'after-change-functions #'nlinum--after-change nil t)
Stefan
źródło
1
Ach ... Właśnie dziś rano myślałem o twojej bibliotece. line-number-at-posMożna zastąpić odpowiedź przez Konstantyna, i że będzie przyspieszyć swoją bibliotekę jeszcze bardziej niż już jej - zwłaszcza w dużych buforów. count-linespowinien również zostać naprawiony przy użyciu metody Constantine. Zastanawiałem się nawet nad przesłaniem skrzynki z sugestiami na infolinię z raportem-emacs-bug, aby naprawić te funkcje.
prawnik