From mboxrd@z Thu Jan 1 00:00:00 1970 From: Alex Kost Subject: [PATCH] emacs: Support auto-updating after performing REPL operation. Date: Tue, 14 Oct 2014 21:45:40 +0400 Message-ID: <87wq825yuz.fsf@gmail.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:36792) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Xe6Af-0004pp-4M for guix-devel@gnu.org; Tue, 14 Oct 2014 13:46:14 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Xe6AW-0005qZ-21 for guix-devel@gnu.org; Tue, 14 Oct 2014 13:46:05 -0400 Received: from mail-pd0-x22f.google.com ([2607:f8b0:400e:c02::22f]:50050) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Xe6AV-0005qD-Hi for guix-devel@gnu.org; Tue, 14 Oct 2014 13:45:55 -0400 Received: by mail-pd0-f175.google.com with SMTP id v10so7922835pde.34 for ; Tue, 14 Oct 2014 10:45:49 -0700 (PDT) Received: from leviafan (128-74-164-83.broadband.corbina.ru. [128.74.164.83]) by mx.google.com with ESMTPSA id ca3sm14730729pbb.80.2014.10.14.10.45.45 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 14 Oct 2014 10:45:48 -0700 (PDT) List-Id: "Development of GNU Guix and the GNU System distribution." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-devel-bounces+gcggd-guix-devel=m.gmane.org@gnu.org Sender: guix-devel-bounces+gcggd-guix-devel=m.gmane.org@gnu.org To: guix-devel@gnu.org --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Hello. Currently when a user performs some REPL operation using Emacs UI =E2=80=93 e.g. installs a package from *Guix Package Info* buffer, information in that buffer stays the same, and he needs to press "g" there to revert the buffer. The attached patches will add that missing feature: after finishing a REPL operation, a buffer from which this operation was performed will be automatically updated. This auto-updating may be disabled with: (setq guix-update-after-operation nil) --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-emacs-Add-hooks-for-REPL-operations.patch >From 108434af7591025101a6bbf82daa484e77ef790b Mon Sep 17 00:00:00 2001 From: Alex Kost Date: Mon, 13 Oct 2014 21:49:31 +0400 Subject: [PATCH 1/2] emacs: Add hooks for REPL operations. * emacs/guix-backend.el (guix-before-repl-operation-hook, guix-after-repl-operation-hook, guix-repl-operation-p): New variables. (guix-repl-operation-success-message, guix-repl-output-filter): New procedures. (guix-start-repl): Adjust for using 'guix-repl-output-filter'. (guix-eval-in-repl): Honor 'guix-before-repl-operation-hook'. --- emacs/guix-backend.el | 47 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/emacs/guix-backend.el b/emacs/guix-backend.el index 46d0f06..0ca9083 100644 --- a/emacs/guix-backend.el +++ b/emacs/guix-backend.el @@ -121,6 +121,22 @@ This REPL is used for receiving information only if (defvar guix-internal-repl-buffer-name "*Guix Internal REPL*" "Default name of an internal Guix REPL buffer.") +(defvar guix-before-repl-operation-hook nil + "Hook run before executing an operation in Guix REPL.") + +(defvar guix-after-repl-operation-hook + '(guix-repl-operation-success-message) + "Hook run after executing successful operation in Guix REPL.") + +(defvar guix-repl-operation-p nil + "Non-nil, if current operation is performed by `guix-eval-in-repl'. +This internal variable is used to distinguish Guix operations +from operations performed in Guix REPL by a user.") + +(defun guix-repl-operation-success-message () + "Message telling about successful Guix operation." + (message "Guix operation has been performed.")) + (defun guix-get-guile-program (&optional internal) "Return a value suitable for `geiser-guile-binary'." (if (or internal @@ -184,12 +200,9 @@ this address (it should be defined by (geiser-impl--set-buffer-implementation impl) (geiser-repl--autodoc-mode -1) (goto-char (point-max)) - (let* ((prompt-re (geiser-repl--prompt-regexp impl)) - (deb-prompt-re (geiser-repl--debugger-prompt-regexp impl)) - (prompt (geiser-con--combined-prompt prompt-re deb-prompt-re))) - (or prompt-re - (error "Oh no! Guix REPL in the buffer '%s' has not been started" - (buffer-name buffer))) + (let ((prompt (geiser-con--combined-prompt + geiser-guile--prompt-regexp + geiser-guile--debugger-prompt-regexp))) (geiser-repl--save-remote-data address) (geiser-repl--start-scheme impl address prompt) (geiser-repl--quit-setup) @@ -199,18 +212,32 @@ this address (it should be defined by (setq geiser-repl--connection (geiser-con--make-connection (get-buffer-process (current-buffer)) - prompt-re - deb-prompt-re)) + geiser-guile--prompt-regexp + geiser-guile--debugger-prompt-regexp)) (geiser-repl--startup impl address) (geiser-repl--autodoc-mode 1) (geiser-company--setup geiser-repl-company-p) (add-hook 'comint-output-filter-functions - 'geiser-repl--output-filter + 'guix-repl-output-filter nil t) (set-process-query-on-exit-flag (get-buffer-process (current-buffer)) geiser-repl-query-on-kill-p))))) +(defun guix-repl-output-filter (str) + "Filter function suitable for `comint-output-filter-functions'. +This is a replacement for `geiser-repl--output-filter'." + (cond + ((string-match-p geiser-guile--prompt-regexp str) + (geiser-autodoc--disinhibit-autodoc) + (when guix-repl-operation-p + (setq guix-repl-operation-p nil) + (run-hooks 'guix-after-repl-operation-hook))) + ((string-match geiser-guile--debugger-prompt-regexp str) + (geiser-con--connection-set-debugging geiser-repl--connection + (match-beginning 0)) + (geiser-autodoc--disinhibit-autodoc)))) + (defun guix-get-repl-buffer (&optional internal) "Return Guix REPL buffer; start REPL if needed. If INTERNAL is non-nil, return an additional internal REPL." @@ -288,6 +315,8 @@ Return elisp expression of the first result value of evaluation." (defun guix-eval-in-repl (str) "Switch to Guix REPL and evaluate STR with guile expression there." + (run-hooks 'guix-before-repl-operation-hook) + (setq guix-repl-operation-p t) (let ((repl (guix-get-repl-buffer))) (with-current-buffer repl (delete-region (geiser-repl--last-prompt-end) (point-max)) -- 2.1.2 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0002-emacs-Support-auto-updating-after-performing-REPL-op.patch >From 4c8117bf517ab820b28a4d2b40a911049097ad55 Mon Sep 17 00:00:00 2001 From: Alex Kost Date: Tue, 14 Oct 2014 20:43:10 +0400 Subject: [PATCH 2/2] emacs: Support auto-updating after performing REPL operation. * emacs/guix-backend.el (guix-operation-buffer): New variable. (guix-eval-in-repl): Use it. Add optional 'operation-buffer' argument. * emacs/guix-base.el (guix-update-after-operation): New variable. (guix-switch-to-buffer, guix-list-or-info-buffer-p, guix-buffers, guix-update-buffers-maybe): New procedures. (guix-set-buffer): Use 'guix-switch-to-buffer'. Add optional 'no-display' argument. (guix-process-package-actions, guix-delete-generations, guix-switch-to-generation): Add optional 'operation-buffer' argument. * emacs/guix-info.el (guix-package-info-insert-action-button, guix-generation-info-insert-number, guix-generation-info-insert-current): Adjust for using operation buffer. * emacs/guix-list.el (guix-list-execute-package-actions, guix-generation-list-switch, guix-generation-list-execute): Likewise. --- emacs/guix-backend.el | 12 ++++++-- emacs/guix-base.el | 83 +++++++++++++++++++++++++++++++++++++++++++-------- emacs/guix-info.el | 12 ++++---- emacs/guix-list.el | 6 ++-- 4 files changed, 89 insertions(+), 24 deletions(-) diff --git a/emacs/guix-backend.el b/emacs/guix-backend.el index 0ca9083..a19d42f 100644 --- a/emacs/guix-backend.el +++ b/emacs/guix-backend.el @@ -272,6 +272,9 @@ additional internal REPL if it exists." ;;; Evaluating expressions +(defvar guix-operation-buffer nil + "Buffer from which the latest Guix operation was performed.") + (defun guix-make-guile-expression (fun &rest args) "Return string containing a guile expression for calling FUN with ARGS." (format "(%S %s)" fun @@ -313,10 +316,13 @@ Return elisp expression of the first result value of evaluation." (replace-regexp-in-string "#t" "t" (car (guix-eval str wrap)))))) -(defun guix-eval-in-repl (str) - "Switch to Guix REPL and evaluate STR with guile expression there." +(defun guix-eval-in-repl (str &optional operation-buffer) + "Switch to Guix REPL and evaluate STR with guile expression there. +If OPERATION-BUFFER is non-nil, it should be a buffer from which +the current operation was performed." (run-hooks 'guix-before-repl-operation-hook) - (setq guix-repl-operation-p t) + (setq guix-repl-operation-p t + guix-operation-buffer operation-buffer) (let ((repl (guix-get-repl-buffer))) (with-current-buffer repl (delete-region (geiser-repl--last-prompt-end) (point-max)) diff --git a/emacs/guix-base.el b/emacs/guix-base.el index 5131eb0..28b1228 100644 --- a/emacs/guix-base.el +++ b/emacs/guix-base.el @@ -191,6 +191,59 @@ If PATH is relative, it is considered to be relative to (recenter 1)))) +;;; Buffers and auto updating. + +(defcustom guix-update-after-operation 'current + "Define what information to update after executing an operation. + +After successful executing an operation in the Guix REPL (for +example after installing a package), information in Guix buffers +will or will not be automatically updated depending on a value of +this variable. + +If nil, update nothing (do not revert any buffer). +If `current', update the buffer from which an operation was performed. +If `all', update all Guix buffers (not recommended)." + :type '(choice (const :tag "Do nothing" nil) + (const :tag "Update operation buffer" current) + (const :tag "Update all Guix buffers" all)) + :group 'guix) + +(defun guix-switch-to-buffer (buffer) + "Switch to a 'list' or 'info' BUFFER." + (pop-to-buffer buffer + '((display-buffer-reuse-window + display-buffer-same-window)))) + +(defun guix-list-or-info-buffer-p (&optional buffer) + "Return non-nil if BUFFER is a Guix 'list' or 'info' buffer. +If BUFFER is nil, check current buffer." + (with-current-buffer (or buffer (current-buffer)) + (derived-mode-p 'guix-list-mode 'guix-info-mode))) + +(defun guix-buffers () + "Return list of all Guix 'list' and 'info' buffers." + (cl-remove-if-not #'guix-list-or-info-buffer-p + (buffer-list))) + +(defun guix-update-buffers-maybe () + "Update buffers after Guix operation if needed. +See `guix-update-after-operation' for details." + (let ((to-update (and guix-operation-buffer + (cl-case guix-update-after-operation + (current (list guix-operation-buffer)) + (all (guix-buffers)))))) + (setq guix-operation-buffer nil) + (mapc (lambda (buf) + (and (buffer-live-p buf) + (guix-list-or-info-buffer-p buf) + (with-current-buffer buf + (guix-revert-buffer nil t)))) + to-update))) + +(add-hook 'guix-after-repl-operation-hook 'guix-update-buffers-maybe) + + ;;; Common definitions for buffer types (defvar-local guix-entries nil @@ -399,16 +452,18 @@ See `guix-get-entries' for the meaning of SEARCH-TYPE and SEARCH-VALS." search-type search-vals))) (defun guix-set-buffer (entries buffer-type entry-type search-type - search-vals &optional history-replace) + search-vals &optional history-replace no-display) "Set up BUFFER-TYPE buffer for displaying ENTRY-TYPE ENTRIES. -Display ENTRIES, set variables and make history item. +Insert ENTRIES in buffer, set variables and make history item. ENTRIES should have a form of `guix-entries'. See `guix-get-entries' for the meaning of SEARCH-TYPE and SEARCH-VALS. If HISTORY-REPLACE is non-nil, replace current history item, -otherwise add the new one." +otherwise add the new one. + +If NO-DISPLAY is non-nil, do not switch to the buffer." (when entries (let ((buf (if (eq major-mode (guix-get-symbol "mode" buffer-type entry-type)) @@ -425,9 +480,8 @@ otherwise add the new one." #'guix-history-replace #'guix-history-add) (guix-make-history-item))) - (pop-to-buffer buf - '((display-buffer-reuse-window - display-buffer-same-window))))) + (or no-display + (guix-switch-to-buffer buf)))) (guix-result-message entries entry-type search-type search-vals)) (defun guix-show-entries (entries buffer-type entry-type) @@ -479,7 +533,7 @@ See `revert-buffer' for the meaning of NOCONFIRM." (guix-get-params-for-receiving guix-buffer-type guix-entry-type)))) (guix-set-buffer entries guix-buffer-type guix-entry-type - guix-search-type guix-search-vals t)))) + guix-search-type guix-search-vals t t)))) (defun guix-redisplay-buffer () "Redisplay current information. @@ -661,7 +715,7 @@ VARIABLE is a name of an option variable.") guix-operation-option-true-string guix-operation-option-false-string)) -(defun guix-process-package-actions (&rest actions) +(defun guix-process-package-actions (actions &optional operation-buffer) "Process package ACTIONS. Each action is a list of the form: @@ -686,7 +740,8 @@ PACKAGE-SPEC should have the following form: (ID [OUTPUT] ...)." 'process-package-actions guix-current-profile :install install :upgrade upgrade :remove remove :use-substitutes? (or guix-use-substitutes 'f) - :dry-run? (or guix-dry-run 'f)))))) + :dry-run? (or guix-dry-run 'f)) + operation-buffer)))) (cl-defun guix-continue-package-operation-p (&key install upgrade remove) "Return non-nil if a package operation should be continued. @@ -802,7 +857,7 @@ Return non-nil, if the operation should be continued; nil otherwise." guix-operation-option-separator))) (force-mode-line-update)) -(defun guix-delete-generations (&rest generations) +(defun guix-delete-generations (generations &optional operation-buffer) "Delete GENERATIONS. Each element from GENERATIONS is a generation number." (when (or (not guix-operation-confirm) @@ -814,16 +869,18 @@ Each element from GENERATIONS is a generation number." (car generations)))))) (guix-eval-in-repl (guix-make-guile-expression - 'delete-generations* guix-current-profile generations)))) + 'delete-generations* guix-current-profile generations) + operation-buffer))) -(defun guix-switch-to-generation (generation) +(defun guix-switch-to-generation (generation &optional operation-buffer) "Switch `guix-current-profile' to GENERATION number." (when (or (not guix-operation-confirm) (y-or-n-p (format "Switch current profile to generation %d? " generation))) (guix-eval-in-repl (guix-make-guile-expression - 'switch-to-generation guix-current-profile generation)))) + 'switch-to-generation guix-current-profile generation) + operation-buffer))) (provide 'guix-base) diff --git a/emacs/guix-info.el b/emacs/guix-info.el index 7a60090..dcd2ce2 100644 --- a/emacs/guix-info.el +++ b/emacs/guix-info.el @@ -558,9 +558,9 @@ ENTRY is an alist with package info." type-str (lambda (btn) (guix-process-package-actions - (list (button-get btn 'action-type) - (list (button-get btn 'id) - (button-get btn 'output))))) + `((,(button-get btn 'action-type) (,(button-get btn 'id) + ,(button-get btn 'output)))) + (current-buffer))) (concat type-str " '" full-name "'") 'action-type type 'id (or (guix-get-key-val entry 'package-id) @@ -639,7 +639,8 @@ ENTRY is an alist with package info." (guix-info-insert-action-button "Delete" (lambda (btn) - (guix-delete-generations (button-get btn 'number))) + (guix-delete-generations (list (button-get btn 'number)) + (current-buffer))) "Delete this generation" 'number number)) @@ -652,7 +653,8 @@ ENTRY is an alist with package info." (guix-info-insert-action-button "Switch" (lambda (btn) - (guix-switch-to-generation (button-get btn 'number))) + (guix-switch-to-generation (button-get btn 'number) + (current-buffer))) "Switch to this generation (make it the current one)" 'number (guix-get-key-val entry 'number)))) diff --git a/emacs/guix-list.el b/emacs/guix-list.el index c3e8ef4..4d3c21c 100644 --- a/emacs/guix-list.el +++ b/emacs/guix-list.el @@ -617,7 +617,7 @@ FUN should accept action-type as argument." (let ((actions (delq nil (mapcar fun '(install delete upgrade))))) (if actions - (apply #'guix-process-package-actions actions) + (guix-process-package-actions actions (current-buffer)) (user-error "No operations specified")))) (defun guix-package-list-execute () @@ -751,7 +751,7 @@ VAL is a boolean value." (number (guix-get-key-val entry 'number))) (if current (user-error "This generation is already the current one") - (guix-switch-to-generation number)))) + (guix-switch-to-generation number (current-buffer))))) (defun guix-generation-list-show-packages () "List installed packages for the generation at point." @@ -773,7 +773,7 @@ With ARG, mark all generations for deletion." (let ((marked (guix-list-get-marked-id-list 'delete))) (or marked (user-error "No generations marked for deletion")) - (apply #'guix-delete-generations marked))) + (guix-delete-generations marked (current-buffer)))) (provide 'guix-list) -- 2.1.2 --=-=-=--