큰 버퍼에서`line-number-at-pos '를 얻는 빠른 방법


19

기능이 line-number-at-pos약 50 회 반복 될 때 포인트가 버퍼의 끝 부분에 가까울 때 세미 대형 버퍼 (예 : 50,000 라인)에서 눈에 띄게 느려집니다. 느려짐에 따라 총 약 1.35 초가 합쳐집니다.

100 % elispfunciton을 사용하여 라인을 계산하고 버퍼의 상단으로 이동하는 대신 모드 라인에 나타나는 라인 번호를 담당하는 내장 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 Manual에 설명 된 % -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로 줄었습니다! 대단히 감사합니다-대단히 감사합니다! :)
Lawlist

6
이 방법을 사용하면 "??"가 나타납니다. 초과 line-number-display-limit-width한 줄 은 여기 에서 알 수 있듯이 기본값 당 200의 값으로 설정됩니다 .
wasamasa

3
IIRC 결과는 마지막 재표시 이후 버퍼에 수정이있는 경우에도 신뢰할 수 없습니다.
Stefan

두 번째 문자 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-posConstantine의 답변으로 대체 될 수 있으며, 특히 큰 버퍼에서 라이브러리의 속도를 기존보다 훨씬 높일 수 있습니다. count-linesConstantine의 방법을 사용하여 수정해야합니다. 나는 그 기능을 고치기 위해 report-emacs-bug 핫라인에 제안서 제출을 보낼 생각조차했다.
lawlist
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.