在大型缓冲区中获取“行中行号”的更快方法


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

Answers:


17

尝试

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

您可以使用《 Emacs Lisp手册》中描述的%-Constructs提取其他信息。

警告:

除了wasamasaStefan指出的限制(请参阅下面的注释),这对于未显示的缓冲区不起作用。

尝试这个:

(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!非常感谢您-非常感谢!:)
律师名单

6
请注意,此方法将为您提供“ ??” line-number-display-limit-width如我在这里发现的,超过的行设置为默认值200 。
wasamasa 2014年

3
如果自上次重新显示以来缓冲区中进行了修改,则IIRC的结果也可能不可靠。
Stefan 2014年

我认为有必要修改答案中的测试,以便将第二个字母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))

在mode函数中使用以下额外配置:

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

1
啊...今天早上我只是在想你的图书馆。该line-number-at-pos会由君士坦丁的答案被替换,这将甚至超过它已经其加快您的图书馆-尤其是在大的缓冲区。 count-lines也应使用君士坦丁的方法进行固定。我什至在考虑将建议框提交的内容发送到report-emacs-bug热线以修复这些功能。
法律列表
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.