Metode yang lebih cepat untuk mendapatkan `line-number-at-pos` dalam buffer besar

19

Fungsi line-number-at-pos(ketika diulang sekitar 50 kali) menyebabkan penurunan yang nyata pada buffer semi-besar - misalnya, 50.000 baris - ketika titik mendekati akhir buffer. Dengan perlambatan, maksud saya total gabungan sekitar 1,35 detik.

Alih-alih menggunakan fungsi 100% elispuntuk menghitung garis dan kebagian atas buffer, saya lebih tertarik pada metode hibrida yang memanfaatkan kemampuan C bawaan yang bertanggung jawab atas nomor baris yang muncul pada mode-line. Nomor baris yang muncul pada mode-line terjadi dengan kecepatan ringan, terlepas dari ukuran buffer.


Berikut ini adalah fungsi tes:

(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))
daftar hukum
sumber

Jawaban:

17

Mencoba

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

Anda dapat mengekstraksi informasi lain menggunakan % -Konstruksi yang dijelaskan dalam Manual Emacs Lisp.

Peringatan:

Selain batasan yang ditunjukkan oleh wasamasa dan Stefan (lihat komentar di bawah) ini tidak berfungsi untuk buffer yang tidak ditampilkan.

Coba ini:

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

dan dibandingkan dengan

(with-temp-buffer
  (dotimes (i 10000)
    (insert (format "%d\n" i)))
  (line-number-at-pos))
Constantine
sumber
Ya, itu mengurangi dari 1,35 detik menjadi 0,003559! Terima kasih banyak - sangat dihargai! :)
hukum
6
Sadarilah bahwa metode ini akan memberi Anda "??" untuk garis yang melebihi line-number-display-limit-widthyang diatur ke nilai 200 per default seperti yang saya temukan di sini .
wasamasa
3
IIRC hasilnya mungkin juga tidak dapat diandalkan jika ada modifikasi dalam buffer sejak ditampilkan ulang terakhir.
Stefan
Saya percaya akan perlu untuk memodifikasi tes dalam jawaban sehingga huruf kedua idiganti dengan (string-to-number (format-mode-line "%l"))untuk tes pertama, dan huruf kedua idiganti dengan (line-number-at-pos)untuk tes kedua.
hukum
5

nlinum.el menggunakan yang berikut:

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

dengan konfigurasi tambahan berikut dalam fungsi mode:

(add-hook 'after-change-functions #'nlinum--after-change nil t)
Stefan
sumber
1
Ah ... Aku baru saja memikirkan perpustakaanmu tadi pagi. Itu line-number-at-posbisa diganti dengan jawaban oleh Constantine, dan itu akan mempercepat perpustakaan Anda bahkan lebih dari yang sudah ada - terutama dalam buffer besar. count-linesjuga harus diperbaiki menggunakan metode oleh Constantine. Saya bahkan berpikir untuk mengirimkan pengiriman kotak saran ke hotline report-emacs-bug untuk memperbaiki fungsi-fungsi tersebut.
hukum