[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
scratch/etags-regen f4a1d47 8/8: Brute force refresh implementation
From: |
Dmitry Gutov |
Subject: |
scratch/etags-regen f4a1d47 8/8: Brute force refresh implementation |
Date: |
Sun, 7 Feb 2021 21:12:03 -0500 (EST) |
branch: scratch/etags-regen
commit f4a1d47327f61da569d81be8f1a7d3c8cd899ffc
Author: Dmitry Gutov <dgutov@yandex.ru>
Commit: Dmitry Gutov <dgutov@yandex.ru>
Brute force refresh implementation
Seems okay-ish in an Emacs checkout (~70ms to refresh), but in GDB
it's 200ms already. Need smarter heuristics.
---
lisp/progmodes/etags-regen.el | 130 +++++++++++++++++++++++++++++++-----------
1 file changed, 98 insertions(+), 32 deletions(-)
diff --git a/lisp/progmodes/etags-regen.el b/lisp/progmodes/etags-regen.el
index aafe78c..c6a7a47 100644
--- a/lisp/progmodes/etags-regen.el
+++ b/lisp/progmodes/etags-regen.el
@@ -32,6 +32,7 @@
(defvar etags-regen--tags-file nil)
(defvar etags-regen--tags-root nil)
+(defvar etags-regen--tags-mtime nil)
(defvar etags-regen--new-file nil)
(declare-function project-root "project")
@@ -113,12 +114,67 @@ File extensions to generate the tags for."
(defvar etags-regen--errors-buffer-name "*etags-regen-tags-errors*")
+(defun etags-regen--all-mtimes (proj)
+ (let ((files (etags-regen--all-files proj))
+ (mtimes (make-hash-table :test 'equal)))
+ (with-temp-buffer
+ (mapc (lambda (f)
+ (insert f "\0"))
+ files)
+ (shell-command-on-region
+ (point-min) (point-max) "xargs -0 stat -c \"%Y\""
+ nil t etags-regen--errors-buffer-name t)
+ (goto-char (point-min))
+ (while (not (eobp))
+ (puthash (pop files)
+ (string-to-number (buffer-substring (point)
(line-end-position)))
+ mtimes)
+ (forward-line 1)))
+ mtimes))
+
+(defun etags-regen--refresh ()
+ (save-excursion
+ (let* ((tags-file-buf (get-file-buffer etags-regen--tags-file))
+ (proj (project-current))
+ (tags-mtime etags-regen--tags-mtime)
+ (all-mtimes (etags-regen--all-mtimes proj))
+ added-files
+ changed-files
+ removed-files)
+ (set-buffer tags-file-buf)
+ (dolist (file (tags-table-files))
+ (let ((mtime (gethash file all-mtimes)))
+ (cond
+ ((null mtime)
+ (push file removed-files))
+ ((> mtime tags-mtime)
+ (push file changed-files)
+ (remhash file all-mtimes))
+ (t
+ (remhash file all-mtimes)))))
+ (maphash
+ (lambda (key _value)
+ (push key added-files))
+ all-mtimes)
+ (when (> (+ (length added-files)
+ (length changed-files)
+ (length removed-files))
+ 100)
+ (message "etags-regen: Too many changes, falling back to full rescan")
+ (etags-regen--tags-cleanup))
+ (dolist (file (nconc removed-files changed-files))
+ (etags-regen--remove-tag file))
+ (when (or changed-files added-files)
+ (apply #'etags-regen--append-tags
+ (nconc changed-files added-files))))))
+
(defun etags-regen--maybe-generate ()
(let (proj)
- (when (and etags-regen--tags-root
- (not (file-in-directory-p default-directory
- etags-regen--tags-root)))
- (etags-regen--tags-cleanup))
+ (when etags-regen--tags-root
+ (if (file-in-directory-p default-directory
+ etags-regen--tags-root)
+ (etags-regen--refresh)
+ (etags-regen--tags-cleanup)))
(when (and (not (or tags-file-name
tags-table-list))
(setq proj (project-current)))
@@ -157,7 +213,8 @@ File extensions to generate the tags for."
(mapconcat #'identity
(etags-regen--build-program-options) " ")
tags-file)))
(setq etags-regen--tags-file tags-file
- etags-regen--tags-root root)
+ etags-regen--tags-root root
+ etags-regen--tags-mtime (time-to-seconds))
(with-temp-buffer
(mapc (lambda (f)
(insert f "\n"))
@@ -183,7 +240,6 @@ File extensions to generate the tags for."
(defun etags-regen--update-file ()
;; TODO: Maybe only do this when Emacs is idle for a bit.
(let ((file-name buffer-file-name)
- (options (etags-regen--build-program-options))
(tags-file-buf (get-file-buffer etags-regen--tags-file))
pr should-scan)
(save-excursion
@@ -197,35 +253,44 @@ File extensions to generate the tags for."
(set-buffer tags-file-buf)
(setq should-scan t))
((progn (set-buffer tags-file-buf)
- (goto-char (point-min))
- (search-forward (format "\f\n%s," file-name) nil t))
- (let ((start (match-beginning 0)))
- (search-forward "\f\n" nil 'move)
- (let ((inhibit-read-only t)
- (save-silently t))
- (delete-region start
- (if (eobp)
- (point)
- (- (point) 2)))))
+ (etags-regen--remove-tag file-name))
(setq should-scan t))))
(when should-scan
- (goto-char (point-max))
- (let ((inhibit-read-only t))
- ;; FIXME: call-process is significantly faster, though.
- ;; Like 10ms vs 20ms here.
- (shell-command
- (format "%s %s %s -o -"
- etags-regen-program (mapconcat #'identity options " ")
- file-name)
- t etags-regen--errors-buffer-name))
- ;; We don't want Emacs to ask us to save the buffer when exiting.
- (set-buffer-modified-p nil)
- ;; FIXME: Is there a better way to do this?
- ;; Completion table is the only remaining place where the
- ;; update is not incremental.
- (setq-default tags-completion-table nil)
+ (etags-regen--append-tags file-name)
))))
+(defun etags-regen--remove-tag (file-name)
+ (goto-char (point-min))
+ (when (search-forward (format "\f\n%s," file-name) nil t)
+ (let ((start (match-beginning 0)))
+ (search-forward "\f\n" nil 'move)
+ (let ((inhibit-read-only t)
+ (save-silently t))
+ (delete-region start
+ (if (eobp)
+ (point)
+ (- (point) 2)))))
+ t))
+
+(defun etags-regen--append-tags (&rest file-names)
+ (goto-char (point-max))
+ (let ((options (etags-regen--build-program-options))
+ (inhibit-read-only t))
+ (setq etags-regen--tags-mtime (time-to-seconds))
+ ;; FIXME: call-process is significantly faster, though.
+ ;; Like 10ms vs 20ms here.
+ (shell-command
+ (format "%s %s %s -o -"
+ etags-regen-program (mapconcat #'identity options " ")
+ (mapconcat #'identity file-names " "))
+ t etags-regen--errors-buffer-name))
+ ;; We don't want Emacs to ask us to save the buffer when exiting.
+ (set-buffer-modified-p nil)
+ ;; FIXME: Is there a better way to do this?
+ ;; Completion table is the only remaining place where the
+ ;; update is not incremental.
+ (setq-default tags-completion-table nil))
+
(defun etags-regen--mark-as-new ()
(unless buffer-file-number
(setq-local etags-regen--new-file t)))
@@ -242,7 +307,8 @@ File extensions to generate the tags for."
(setq tags-file-name nil
tags-table-list nil
etags-regen--tags-file nil
- etags-regen--tags-root nil))
+ etags-regen--tags-root nil
+ etags-regen--tags-mtime nil))
(remove-hook 'after-save-hook #'etags-regen--update-file)
(remove-hook 'before-save-hook #'etags-regen--mark-as-new))
- scratch/etags-regen updated (153a549 -> f4a1d47), Dmitry Gutov, 2021/02/07
- scratch/etags-regen 8d00e2f 1/8: Merge branch 'master' into scratch/etags-regen, Dmitry Gutov, 2021/02/07
- scratch/etags-regen 1daad17 2/8: Merge branch 'master' into scratch/etags-regen, Dmitry Gutov, 2021/02/07
- scratch/etags-regen 44f19c7 3/8: Merge branch 'master' into scratch/etags-regen, Dmitry Gutov, 2021/02/07
- scratch/etags-regen 25b2915 7/8: Introduce project-files-filtered and use it, Dmitry Gutov, 2021/02/07
- scratch/etags-regen f4a1d47 8/8: Brute force refresh implementation,
Dmitry Gutov <=
- scratch/etags-regen 3098e47 4/8: Merge branch 'master' into scratch/etags-regen, Dmitry Gutov, 2021/02/07
- scratch/etags-regen 4f7b533 6/8: etags-regen--all-files: Extract to a separate function, Dmitry Gutov, 2021/02/07
- scratch/etags-regen f520e5d 5/8: Merge branch 'master' into scratch/etags-regen, Dmitry Gutov, 2021/02/07