Eine schnellere Methode, um die Zeilennummer in großen Puffern zu ermitteln

19

Die Funktion line-number-at-pos(wenn sie ungefähr 50 Mal wiederholt wird) bewirkt eine merkliche Verlangsamung in halbgroßen Puffern - z. B. 50.000 Zeilen -, wenn sich der Punkt nahe dem Ende des Puffers befindet. Mit Verlangsamung meine ich insgesamt ungefähr 1,35 Sekunden.

Anstatt eine 100% elisp-Funktion zu verwenden, um Zeilen zu zählen und an die Spitze des Puffers zu gelangen, wäre ich an einer Hybridmethode interessiert, die die eingebauten C-Fähigkeiten nutzt, die für die Zeilennummer verantwortlich sind, die auf der Moduszeile erscheint. Die Zeilennummer, die in der Moduszeile erscheint, wird unabhängig von der Größe des Puffers mit Lichtgeschwindigkeit angezeigt.


Hier ist eine Testfunktion:

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

Antworten:

17

Versuchen

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

Sie können andere Informationen mit % -Constructs extrahieren, die im Emacs Lisp-Handbuch beschrieben sind.

Vorbehalt:

Zusätzlich zu den Einschränkungen, auf die wasamasa und Stefan hingewiesen haben (siehe Kommentare unten), funktioniert dies nicht für Puffer, die nicht angezeigt werden.

Versuche dies:

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

und vergleiche mit

(with-temp-buffer
  (dotimes (i 10000)
    (insert (format "%d\n" i)))
  (line-number-at-pos))
Konstantin
quelle
Ja, das hat es von 1,35 Sekunden auf 0,003559 reduziert! Vielen Dank - sehr geschätzt! :)
Gesetzesliste
6
Seien Sie sich bewusst, dass diese Methode Ihnen "??" für Zeilen, line-number-display-limit-widthdie einen Wert von 200 überschreiten , wie ich hier herausgefunden habe .
Wasamasa
3
IIRC Das Ergebnis kann auch dann unzuverlässig sein, wenn seit der letzten erneuten Anzeige Änderungen im Puffer vorgenommen wurden.
Stefan
Ich glaube , es wäre notwendig , die Tests in der Antwort , so dass der zweite Buchstabe zu ändern iist mit ersetzt (string-to-number (format-mode-line "%l"))für den ersten Test, und der zweite Buchstabe iwird mit ersetzt (line-number-at-pos)für den zweiten Test.
Gesetzesliste
5

nlinum.el verwendet Folgendes:

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

mit folgender extra konfiguration in der mode funktion:

(add-hook 'after-change-functions #'nlinum--after-change nil t)
Stefan
quelle
1
Ah ... Ich habe heute Morgen gerade an deine Bibliothek gedacht. Das line-number-at-poskönnte durch die Antwort von Constantine ersetzt werden, und das würde Ihre Bibliothek noch mehr beschleunigen, als es bereits ist - besonders in großen Puffern. count-linessollte auch mit der Methode von Constantine behoben werden. Ich dachte sogar daran, eine Suggest-Box-Einsendung an die Hotline für Report-Emacs-Fehler zu senden, um diese Funktionen zu reparieren.
Gesetzesliste