branch: externals/gtags-mode commit 9763f8fb69458432edd670b0f4074882a0cc0ee3 Author: Jimmy Aguilar Mena <kratsbinov...@gmail.com> Commit: Jimmy Aguilar Mena <kratsbinov...@gmail.com>
Add async processes to create and update database cleanly. --- global-xref.el | 132 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 82 insertions(+), 50 deletions(-) diff --git a/global-xref.el b/global-xref.el index 12e4f0b958..52092e0f4a 100644 --- a/global-xref.el +++ b/global-xref.el @@ -59,32 +59,59 @@ (forward-line 1)) (nreverse lines))) +(defvar global-xref--roots-list nil) + (defvar-local global-xref--global (executable-find global-xref-global)) (defvar-local global-xref--gtags (executable-find global-xref-gtags)) - (defvar-local global-xref--project-root nil "Project Global root for this buffer.") -(defun global-xref--process-file (args &optional postfunction) - "Run GNU Global with `process-file' and ARGS. -Return the output as a string or passes it to POSTFUNCTION. -Return nil if an error occurred." +(defun global-xref--exec (command args async postfunction) + "Run COMMAND-SYM with and ARGS, in ASYNC way. +When ASYNC is 'nil' executes command synchronously; returns the +output of the command as a string or calls POSTFUNCTION in the +command's buffer and returns the result. returns nil if an error +occurred. +When ASYNC is non-nil starts an async process and executes the +POSTFUNCTION in a sentinel. Returns the process handler in all +the cases" (with-connection-local-variables - (when global-xref-global - (with-temp-buffer - (let ((status (apply #'process-file global-xref--global nil (current-buffer) nil args))) - (if (eq status 0) - (if (functionp postfunction) - (funcall postfunction) - (string-trim (buffer-substring-no-properties (point-min) (point-max)))) - (error "%s exited with status %s" global-xref--global status) - (let ((inhibit-message t)) - (message "global error output:\n%s" (buffer-string))) - nil)))))) + (when-let (cmd (symbol-value command)) + (if async ;; When async + (let ((process (apply #'start-file-process + (format "%s-async" cmd) + (generate-new-buffer " *temp*" t) cmd args))) + (set-process-sentinel + process + (lambda (process event) + (if-let* (((eq (process-status process) 'exit)) + (temp-buffer (process-buffer process))) + (with-current-buffer temp-buffer + (while (accept-process-output process)) + (when (functionp postfunction) + (unwind-protect + (funcall postfunction) + (and (buffer-name temp-buffer) + (kill-buffer temp-buffer))))) + (let ((inhibit-message t)) + (message "global error output:\n%s" (buffer-string)))) + (message "Async %s: %s" (process-command process) event))) + process) + + (with-temp-buffer ;; When sync + (let ((status (apply #'process-file cmd nil (current-buffer) nil args))) + (if (eq status 0) + (if (functionp postfunction) + (funcall postfunction) + (string-trim (buffer-substring-no-properties (point-min) (point-max)))) + (error "Sync %s: exited abnormally with code %s" cmd status) + (let ((inhibit-message t)) + (message "global error output:\n%s" (buffer-string))) + nil))))))) (defsubst global-xref--to-list (args) "Run GNU Global with `process-file' and ARGS return a list." - (global-xref--process-file args #'global-xref--buffer-to-list)) + (global-xref--exec 'global-xref--global args nil #'global-xref--buffer-to-list)) (defun global-xref--set-connection-locals () "Set GLOBAL connection local variables when possible." @@ -102,48 +129,47 @@ Return nil if an error occurred." (defun global-xref--find-root () "Return the GLOBAL project root. Return nil if none." - (global-xref--process-file '("--print-dbpath"))) + (let ((root (global-xref--exec 'global-xref--global '("--print-dbpath") nil nil))) + (when root + (add-to-list 'global-xref--roots-list root) + root))) (defun global-xref--filter-find-symbol (creator args symbol) "Run GNU Global and apply CREATOR to global-xref--to-list output. Return the results as a list." - (remove nil - (mapcar (lambda (gtags-x-line) - (when (string-match - "^\\([^ \t]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([^ \t\]+\\)[ \t]+\\(.*\\)" - gtags-x-line) - (funcall creator - (match-string 1 gtags-x-line) ;; name - (match-string 4 gtags-x-line) ;; code - (match-string 3 gtags-x-line) ;; file - (string-to-number (match-string 2 gtags-x-line)) ;; line - ))) - (global-xref--to-list - (append args (list "--result=ctags-x" "--path-style=absolute" - (shell-quote-argument symbol))))))) + (remove + nil + (mapcar (lambda (gtags-x-line) + (when (string-match + "^\\([^ \t]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([^ \t\]+\\)[ \t]+\\(.*\\)" + gtags-x-line) + (funcall creator + (match-string 1 gtags-x-line) ;; name + (match-string 4 gtags-x-line) ;; code + (match-string 3 gtags-x-line) ;; file + (string-to-number (match-string 2 gtags-x-line)) ;; line + ))) + (global-xref--to-list + (append args (list "--result=ctags-x" "--path-style=absolute" + (shell-quote-argument symbol))))))) ;; Interactive commands. (defun global-xref-create-db (root-dir) - "Create a GLOBAL database in ROOT-DIR." + "Create a GLOBAL database in ROOT-DIR asynchronously." (interactive "DCreate db in directory: ") - (let ((default-directory root-dir) - (buffer (get-buffer-create " *Global-Xref create*"))) - (with-connection-local-variables - (set-process-sentinel - (start-file-process "global-xref-create-db" buffer global-xref--gtags) - (lambda (process event) - (message "Global created %s: %s" root-dir (string-trim event)) - ;; If not GLOBAL root, maybe we can set it after this. - (when (and (eq (process-status process) 'exit) - (not global-xref--project-root)) - (setq global-xref--project-root (global-xref--find-root)) - (message "Set GLOBAL project-root: %s" global-xref--project-root))))))) + (let ((default-directory root-dir)) + (global-xref--exec + 'global-xref--gtags '("--update") t + (lambda () + (when (not global-xref--project-root) + (setq global-xref--project-root (global-xref--find-root)) + (message "Set GLOBAL project-root: %s" global-xref--project-root)))))) (defun global-xref-update () "Update GLOBAL project database." (interactive) (if global-xref--project-root - (global-xref--process-file '("--update")) + (global-xref--exec 'global-xref--global '("--update") t t) (error "Not under a GLOBAL project"))) (defun global-xref-update-file (file) @@ -152,14 +178,21 @@ Return the results as a list." (if (and global-xref--project-root (file-exists-p file) (string-prefix-p global-xref--project-root (expand-file-name file))) - (global-xref--process-file (list "--single-update" file)) - (error "Root: %s Buffer file: %s" global-xref--project-root file))) + (global-xref--exec 'global-xref--global (list "--single-update" file) t t) + (when (called-interactively-p 'all) + (error "Database no updated Root: %s Buffer file: %s" + global-xref--project-root file)))) (defun global-xref--after-save-hook () "After save hook to update GLOBAL database with changed data." (when buffer-file-name (global-xref-update-file buffer-file-name))) +(defun global-xref--find-file-hook () + "After save hook to update GLOBAL database with changed data." + (when buffer-file-name + (global-xref-update-file buffer-file-name))) + ;; xref integration (defun global-xref--find-symbol (args symbol) "Run GNU Global to create xref input list with ARGS on SYMBOL. @@ -221,8 +254,7 @@ any additional command line arguments to pass to GNU Global." (add-hook 'xref-backend-functions #'global-xref-xref-backend nil t) (add-hook 'after-save-hook #'global-xref--after-save-hook nil t) (setq global-xref--imenu-default-function imenu-create-index-function) - (setq imenu-create-index-function #'global-xref-imenu-create-index-function) - ) + (setq imenu-create-index-function #'global-xref-imenu-create-index-function)) (t (setq global-xref--project-root nil) (remove-hook 'xref-backend-functions #'global-xref-xref-backend t)