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

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

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)

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.