all messages for Guix-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Alex Kost <alezost@gmail.com>
To: guix-devel@gnu.org
Subject: [PATCH] emacs: Support auto-updating after performing REPL operation.
Date: Tue, 14 Oct 2014 21:45:40 +0400	[thread overview]
Message-ID: <87wq825yuz.fsf@gmail.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 497 bytes --]

Hello.

Currently when a user performs some REPL operation using Emacs UI –
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)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-emacs-Add-hooks-for-REPL-operations.patch --]
[-- Type: text/x-diff, Size: 4629 bytes --]

From 108434af7591025101a6bbf82daa484e77ef790b Mon Sep 17 00:00:00 2001
From: Alex Kost <alezost@gmail.com>
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


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-emacs-Support-auto-updating-after-performing-REPL-op.patch --]
[-- Type: text/x-diff, Size: 11897 bytes --]

From 4c8117bf517ab820b28a4d2b40a911049097ad55 Mon Sep 17 00:00:00 2001
From: Alex Kost <alezost@gmail.com>
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."
 \f
 ;;; 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))))
 
 \f
+;;; 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)
+
+\f
 ;;; 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


             reply	other threads:[~2014-10-14 17:46 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-10-14 17:45 Alex Kost [this message]
2014-10-14 19:17 ` [PATCH] emacs: Support auto-updating after performing REPL operation Ludovic Courtès
2014-10-15  6:30   ` Alex Kost
2014-10-15 19:32     ` Ludovic Courtès
2014-10-15 19:34     ` Ludovic Courtès
2014-10-14 19:22 ` Ludovic Courtès
2014-10-15  6:30   ` Alex Kost
2014-10-15 19:33     ` Ludovic Courtès

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87wq825yuz.fsf@gmail.com \
    --to=alezost@gmail.com \
    --cc=guix-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/guix.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.