大きなバッファーで `line-number-at-pos`を取得するより高速な方法


19

この機能line-number-at-pos(約50回繰り返した場合)は、ポイントがバッファーの終わり近くにあるときに、セミラインバッファー(たとえば50,000行)で顕著なスローダウンを引き起こしています。スローダウンとは、合計で約1.35秒を意味します。

100%のelisp関数を使用して行をカウントし、バッファーの先頭に移動する代わりに、モード行に表示される行番号に関与する組み込みのC機能を活用するハイブリッドメソッドに興味があります。モードラインに表示される行番号は、バッファのサイズに関係なく、軽い速度で発生します。


テスト関数は次のとおりです。

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

回答:


17

試して

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

Emacs Lispマニュアルで説明されている%-Constructsを使用して、他の情報を抽出できます。

警告:

また、制限のことで指摘wasamasaステファンこれが表示されていないバッファの仕事をしません(下のコメントを参照してください)。

これを試して:

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

と比較する

(with-temp-buffer
  (dotimes (i 10000)
    (insert (format "%d\n" i)))
  (line-number-at-pos))

はい、1.35秒から0.003559に短縮しました!どうもありがとうございました-非常に感謝します!:)
法律家14年

6
この方法で「??」が得られることに注意してください。line-number-display-limit-width私がここで見つけたように、デフォルトごとに200の値に設定されている行を超えています
wasamasa 14年

3
また、IIRCでは、最後の再表示以降にバッファに変更があった場合、結果が信頼できない場合があります。
ステファン14年

最初のテストでは2番目の文字が置き換えられ、2番目のテストでは2番目の文字iが置き換えられるように、回答のテストを変更する必要があると思います。(string-to-number (format-mode-line "%l"))i(line-number-at-pos)
法律家

5

nlinum.elは次を使用します。

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

モード機能に次の追加設定を使用します。

(add-hook 'after-change-functions #'nlinum--after-change nil t)

1
ああ...私は今朝早くあなたの図書館について考えていました。line-number-at-posコンスタンティンによって答えを置き換えることができ、そしてそれはさらにそれよりも、すでにそのライブラリをスピードアップするだろう-特に大きなバッファインチ count-linesConstantineのメソッドを使用して修正する必要もあります。それらの機能を修正するために、report-emacs-bugホットラインに提案ボックスを送信することさえ考えていました。
法律家14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.