all messages for Guix-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Alex Kost <alezost@gmail.com>
To: "Ludovic Courtès" <ludo@gnu.org>
Cc: guix-devel@gnu.org
Subject: Re: [PATCH 3/3] emacs: Add "Source" field to 'guix-info' buffers.
Date: Tue, 11 Nov 2014 22:13:54 +0300	[thread overview]
Message-ID: <877fz14kjx.fsf@gmail.com> (raw)
In-Reply-To: <87y4riiqhf.fsf@gnu.org> ("Ludovic \=\?utf-8\?Q\?Court\=C3\=A8s\=22'\?\= \=\?utf-8\?Q\?s\?\= message of "Tue, 11 Nov 2014 00:29:48 +0100")

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

Ludovic Courtès (2014-11-11 02:29 +0300) wrote:

> Alex Kost <alezost@gmail.com> skribis:
>
>> Ludovic Courtès (2014-11-10 01:43 +0300) wrote:

[...]

>>>>> With the interface you propose, things might be slightly confusing:
>>>>> sometimes clicking on “Download” will do nothing (because the source is
>>>>> already there), sometime “Show” will work without “Download”, sometimes
>>>>> not, etc.  Also it takes up quite a bit of space.
>>>>
>>>> Sorry, I didn't get it.  What space do you mean?
>>>
>>> I meant visual space in the user interface; visual clutter.
>>
>> Is that "too much information is bad"?  Do you suggest to remove
>> something?
>
> I think a single button is clearer than two buttons plus a label, but
> that’s OK here since we can’t really do just one button.

What can be done is: if a source is not in the store after pressing a
“Show” button, a user may be asked whether he wants to download it or
not.  I think it should be less confusing (the modified patch is
attached).  WDYT?

>>> But note that derivation outputs not obtained by a ‘build-derivations’
>>> call with the current store connection may be garbage-collected anytime.
>>> That makes it more difficult to reliably determine whether the
>>> “Download” button should be displayed; to be safe, it would have to be
>>> displayed by default.  Then we’re very close to the current patch, I
>>> think, no?
>>
>> I just check whether a final source file exists in the store and that's
>> all.  I think it's reliable, isn't it?
>
> There’s still the possibility that (1) the source is there, so no
> “Download” button, (2) the source is GC’d, and (3) there’s still no
> “Download” button and trying to access the source fails gracelessly.
>
> That’s probably not very common in practice, and easily fixed by hitting
> ‘g’, so maybe it’s not worth worrying.  WDYT?

Do you mean a user deleted the source (with "guix gc") when a “package
info” buffer was displayed?  Sure such cases are not (and I think cannot
be) handled.  It's the same as if:

1) a user has a list of installed packages,

2) installs another package somewhere outside (e.g., in a shell with
"guix package -i"),

3) and wonders why the list is not up-to-date.

Of course it is not up-to-date!  He needs to revert a buffer after that.

[...]

>> +(define (package-source-names package)
>> +  "Return a list of source names (URLs) of the PACKAGE."
>> +  (let ((source (package-source package)))
>> +    (and (origin? source)
>> +         (filter-map (lambda (uri)
>> +                       (cond ((string? uri)
>> +                              uri)
>> +                             ((git-reference? uri)
>> +                              (git-reference-url uri))
>> +                             (else #f)))
>> +                     (list-maybe (origin-uri source))))))
>
> The #f case above just leads to degraded display, not breakage, right?
> (I’m asking because of the other things beyond string? and
> git-reference?.)

Yes, there _would_ be just "Source: –", but it will not happen because
there are no other things beyond a string URL and a git-reference URL.
So the source field will be empty only for packages with a false source
(like ‘mit-scheme’ or bootstrap packages).  Sources for all other
packages will be displayed.  You may check it by evaluating the
following:

(setcdr (assq 'output guix-list-column-format)
        '((name 20 t)
          (version 10 t)
          (output 9 t)
          (installed 12 t)
          (source 30 t)))

then "M-x guix-all-available-packages" and sort by the "Source" column.


[-- Attachment #2: 0001-emacs-Add-Source-field-to-guix-info-buffers.patch --]
[-- Type: text/x-diff, Size: 15462 bytes --]

From 733c5276bcb9ded008e9c0a4dbe2e5fb6561b5eb Mon Sep 17 00:00:00 2001
From: Alex Kost <alezost@gmail.com>
Date: Sun, 9 Nov 2014 11:03:39 +0300
Subject: [PATCH] emacs: Add "Source" field to 'guix-info' buffers.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Suggested by Ludovic Courtès.

* emacs/guix-info.el (guix-info-insert-methods, guix-info-displayed-params):
  Add 'source' parameter.
  (guix-package-info-source): New face.
  (guix-package-source): New button type.
  (guix-package-info-auto-find-source, guix-package-info-auto-download-source,
  guix-package-info-download-buffer): New variables.
  (guix-package-info-show-source, guix-package-info-insert-source-url,
  guix-package-info-insert-source, guix-package-info-download-source,
  guix-package-info-redisplay-after-download): New procedures.
* emacs/guix-base.el (guix-param-titles): Add 'source' parameter.
  (guix-operation-prompt): Add 'prompt' argument.
  (guix-after-source-download-hook): New variable.
  (guix-package-source-path, guix-package-source-build-derivation): New
  procedures.
* emacs/guix-main.scm (%package-param-alist): Add 'source' parameter.
  (package-source-names, package-source-derivation->store-path,
  package-source-path, package-source-build-derivation): New procedures.
---
 emacs/guix-base.el  |  33 +++++++++++--
 emacs/guix-info.el  | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 emacs/guix-main.scm |  49 ++++++++++++++++++++
 3 files changed, 204 insertions(+), 8 deletions(-)

diff --git a/emacs/guix-base.el b/emacs/guix-base.el
index a6e56dc..23575ac 100644
--- a/emacs/guix-base.el
+++ b/emacs/guix-base.el
@@ -82,6 +82,7 @@ Interactively, prompt for PATH.  With prefix, use
      (id                . "ID")
      (name              . "Name")
      (version           . "Version")
+     (source            . "Source")
      (license           . "License")
      (synopsis          . "Synopsis")
      (description       . "Description")
@@ -100,6 +101,7 @@ Interactively, prompt for PATH.  With prefix, use
      (id                . "ID")
      (name              . "Name")
      (version           . "Version")
+     (source            . "Source")
      (license           . "License")
      (synopsis          . "Synopsis")
      (description       . "Description")
@@ -954,13 +956,14 @@ ENTRIES is a list of package entries to get info about packages."
           strings)
     (insert "\n")))
 
-(defun guix-operation-prompt ()
+(defun guix-operation-prompt (&optional prompt)
   "Prompt a user for continuing the current operation.
-Return non-nil, if the operation should be continued; nil otherwise."
+Return non-nil, if the operation should be continued; nil otherwise.
+Ask a user with PROMPT for continuing an operation."
   (let* ((option-keys (mapcar #'guix-operation-option-key
                               guix-operation-options))
          (keys (append '(?y ?n) option-keys))
-         (prompt (concat (propertize "Continue operation?"
+         (prompt (concat (propertize (or prompt "Continue operation?")
                                      'face 'minibuffer-prompt)
                          " ("
                          (mapconcat
@@ -1035,6 +1038,30 @@ Each element from GENERATIONS is a generation number."
       'switch-to-generation profile generation)
      operation-buffer)))
 
+(defun guix-package-source-path (package-id)
+  "Return a store file path to a source of a package PACKAGE-ID."
+  (message "Calculating the source derivation ...")
+  (guix-eval-read
+   (guix-make-guile-expression
+    'package-source-path package-id)))
+
+(defvar guix-after-source-download-hook nil
+  "Hook run after successful performing a 'source-download' operation.")
+
+(defun guix-package-source-build-derivation (package-id &optional prompt)
+  "Build source derivation of a package PACKAGE-ID.
+Ask a user with PROMPT for continuing an operation."
+  (when (or (not guix-operation-confirm)
+            (guix-operation-prompt (or prompt
+                                       "Build the source derivation?")))
+    (guix-eval-in-repl
+     (guix-make-guile-expression
+      'package-source-build-derivation
+      package-id
+      :use-substitutes? (or guix-use-substitutes 'f)
+      :dry-run? (or guix-dry-run 'f))
+     nil 'source-download)))
+
 \f
 ;;; Pull
 
diff --git a/emacs/guix-info.el b/emacs/guix-info.el
index 70ae39c..cbf8f46 100644
--- a/emacs/guix-info.el
+++ b/emacs/guix-info.el
@@ -1,4 +1,4 @@
-;;; guix-info.el --- Info buffers for displaying entries
+;;; guix-info.el --- Info buffers for displaying entries   -*- lexical-binding: t -*-
 
 ;; Copyright © 2014 Alex Kost <alezost@gmail.com>
 
@@ -24,7 +24,6 @@
 
 ;;; Code:
 
-(require 'guix-history)
 (require 'guix-base)
 (require 'guix-utils)
 
@@ -107,6 +106,8 @@ number of characters, it will be split into several lines.")
                         guix-info-insert-title-simple)
      (outputs           guix-package-info-insert-outputs
                         guix-info-insert-title-simple)
+     (source            guix-package-info-insert-source
+                        guix-info-insert-title-simple)
      (home-url          guix-info-insert-url)
      (inputs            guix-package-info-insert-inputs)
      (native-inputs     guix-package-info-insert-native-inputs)
@@ -121,6 +122,8 @@ number of characters, it will be split into several lines.")
      (name              guix-package-info-name)
      (version           guix-output-info-insert-version)
      (output            guix-output-info-insert-output)
+     (source            guix-package-info-insert-source
+                        guix-info-insert-title-simple)
      (path              guix-package-info-insert-output-path
                         guix-info-insert-title-simple)
      (dependencies      guix-package-info-insert-output-dependencies
@@ -157,10 +160,11 @@ is a function, this function is called with parameter title as
 argument.")
 
 (defvar guix-info-displayed-params
-  '((package name version synopsis outputs location home-url
+  '((package name version synopsis outputs source location home-url
              license inputs native-inputs propagated-inputs description)
-    (output name version output synopsis path dependencies location home-url
-            license inputs native-inputs propagated-inputs description)
+    (output name version output synopsis source path dependencies location
+            home-url license inputs native-inputs propagated-inputs
+            description)
     (installed path dependencies)
     (generation number prev-number current time path))
   "List of displayed entry parameters.
@@ -652,6 +656,122 @@ ENTRY is an alist with package info."
   'guix-package-info-insert-output-path)
 
 \f
+;;; Inserting a source
+
+(defface guix-package-info-source
+  '((t :inherit link :underline nil))
+  "Face used for a source URL of a package."
+  :group 'guix-package-info)
+
+(defcustom guix-package-info-auto-find-source nil
+  "If non-nil, find a source file after pressing a \"Show\" button.
+If nil, just display the source file path without finding."
+  :type 'boolean
+  :group 'guix-package-info)
+
+(defcustom guix-package-info-auto-download-source t
+  "If nil, do not automatically download a source file if it doesn't exist.
+After pressing a \"Show\" button, a derivation of the package
+source is calculated and a store file path is displayed.  If this
+variable is non-nil and the source file does not exist in the
+store, it will be automatically downloaded (with a possible
+prompt depending on `guix-operation-confirm' variable)."
+  :type 'boolean
+  :group 'guix-package-info)
+
+(defvar guix-package-info-download-buffer nil
+  "Buffer from which a current download operation was performed.")
+
+(define-button-type 'guix-package-source
+  :supertype 'guix
+  'face 'guix-package-info-source
+  'help-echo ""
+  'action (lambda (_)
+            ;; As a source may not be a real URL (e.g., "mirror://..."),
+            ;; no action is bound to a source button.
+            (message "Yes, this is the source URL. What did you expect?")))
+
+(defun guix-package-info-insert-source-url (url &optional _)
+  "Make button from source URL and insert it at point."
+  (guix-insert-button url 'guix-package-source))
+
+(defun guix-package-info-show-source (entry-id package-id)
+  "Show file name of a package source in the current info buffer.
+Find the file if needed (see `guix-package-info-auto-find-source').
+ENTRY-ID is an ID of the current entry (package or output).
+PACKAGE-ID is an ID of the package which source to show."
+  (let* ((entry (guix-get-entry-by-id entry-id guix-entries))
+         (file  (guix-package-source-path package-id)))
+    (or file
+        (error "Couldn't define file path of the package source"))
+    (let* ((new-entry (cons (cons 'source-file file)
+                            entry))
+           (entries (cl-substitute-if
+                     new-entry
+                     (lambda (entry)
+                       (equal (guix-get-key-val entry 'id)
+                              entry-id))
+                     guix-entries
+                     :count 1)))
+      (guix-redisplay-buffer :entries entries)
+      (if (file-exists-p file)
+          (if guix-package-info-auto-find-source
+              (guix-find-file file)
+            (message "The source store path is displayed."))
+        (if guix-package-info-auto-download-source
+            (guix-package-info-download-source package-id)
+          (message "The source does not exist in the store."))))))
+
+(defun guix-package-info-download-source (package-id)
+  "Download a source of the package PACKAGE-ID."
+  (setq guix-package-info-download-buffer (current-buffer))
+  (guix-package-source-build-derivation
+   package-id
+   "The source does not exist in the store. Download it?"))
+
+(defun guix-package-info-insert-source (source entry)
+  "Insert SOURCE from package ENTRY at point.
+SOURCE is a list of URLs."
+  (guix-info-insert-indent)
+  (if (null source)
+      (guix-format-insert nil)
+    (let* ((source-file (guix-get-key-val entry 'source-file))
+           (entry-id    (guix-get-key-val entry 'id))
+           (package-id  (or (guix-get-key-val entry 'package-id)
+                            entry-id)))
+      (if (null source-file)
+          (guix-info-insert-action-button
+           "Show"
+           (lambda (btn)
+             (guix-package-info-show-source (button-get btn 'entry-id)
+                                            (button-get btn 'package-id)))
+           "Show the source store path of the current package"
+           'entry-id entry-id
+           'package-id package-id)
+        (unless (file-exists-p source-file)
+          (guix-info-insert-action-button
+           "Download"
+           (lambda (btn)
+             (guix-package-info-download-source
+              (button-get btn 'package-id)))
+           "Download the source into the store"
+           'package-id package-id))
+        (guix-info-insert-val-simple source-file
+                                     #'guix-info-insert-file-path))
+      (guix-info-insert-val-simple source
+                                   #'guix-package-info-insert-source-url))))
+
+(defun guix-package-info-redisplay-after-download ()
+  "Redisplay an 'info' buffer after downloading the package source.
+This function is used to hide a \"Download\" button if needed."
+  (when (buffer-live-p guix-package-info-download-buffer)
+    (guix-redisplay-buffer :buffer guix-package-info-download-buffer)
+    (setq guix-package-info-download-buffer nil)))
+
+(add-hook 'guix-after-source-download-hook
+          'guix-package-info-redisplay-after-download)
+
+\f
 ;;; Displaying outputs
 
 (guix-define-buffer-type info output
diff --git a/emacs/guix-main.scm b/emacs/guix-main.scm
index 62eeabb..e0bdccb 100644
--- a/emacs/guix-main.scm
+++ b/emacs/guix-main.scm
@@ -46,10 +46,12 @@
  (ice-9 vlist)
  (ice-9 match)
  (srfi srfi-1)
+ (srfi srfi-2)
  (srfi srfi-11)
  (srfi srfi-19)
  (srfi srfi-26)
  (guix)
+ (guix git-download)
  (guix packages)
  (guix profiles)
  (guix licenses)
@@ -252,6 +254,18 @@ Example:
                      (license-name license)))
               (list-maybe (package-license package))))
 
+(define (package-source-names package)
+  "Return a list of source names (URLs) of the PACKAGE."
+  (let ((source (package-source package)))
+    (and (origin? source)
+         (filter-map (lambda (uri)
+                       (cond ((string? uri)
+                              uri)
+                             ((git-reference? uri)
+                              (git-reference-url uri))
+                             (else #f)))
+                     (list-maybe (origin-uri source))))))
+
 (define (package-unique? package)
   "Return #t if PACKAGE is a single package with such name/version."
   (null? (cdr (packages-by-name (package-name package)
@@ -263,6 +277,7 @@ Example:
     (name              . ,package-name)
     (version           . ,package-version)
     (license           . ,package-license-names)
+    (source            . ,package-source-names)
     (synopsis          . ,package-synopsis)
     (description       . ,package-description)
     (home-url          . ,package-home-page)
@@ -867,3 +882,37 @@ OUTPUTS is a list of package outputs (may be an empty list)."
 GENERATIONS is a list of generation numbers."
   (with-store store
     (delete-generations store profile generations)))
+
+(define (package-source-derivation->store-path derivation)
+  "Return a store path of the package source DERIVATION."
+  (match (derivation-outputs derivation)
+    ;; Source derivation is always (("out" . derivation)).
+    (((_ . output-drv))
+     (derivation-output-path output-drv))
+    (_ #f)))
+
+(define (package-source-path package-id)
+  "Return a store file path to a source of a package PACKAGE-ID."
+  (and-let* ((package (package-by-id package-id))
+             (source  (package-source package)))
+    (with-store store
+      (package-source-derivation->store-path
+       (package-source-derivation store source)))))
+
+(define* (package-source-build-derivation package-id #:key dry-run?
+                                          (use-substitutes? #t))
+  "Build source derivation of a package PACKAGE-ID."
+  (and-let* ((package (package-by-id package-id))
+             (source  (package-source package)))
+    (with-store store
+      (let* ((derivation  (package-source-derivation store source))
+             (derivations (list derivation)))
+        (set-build-options store
+                           #:use-substitutes? use-substitutes?)
+        (show-what-to-build store derivations
+                            #:use-substitutes? use-substitutes?
+                            #:dry-run? dry-run?)
+        (unless dry-run?
+          (build-derivations store derivations))
+        (format #t "The source store path: ~a~%"
+                (package-source-derivation->store-path derivation))))))
-- 
2.1.2


  reply	other threads:[~2014-11-11 19:14 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-11-09  8:40 [PATCH 3/3] emacs: Add "Source" field to 'guix-info' buffers Alex Kost
2014-11-09 17:45 ` Ludovic Courtès
2014-11-09 18:48   ` Alex Kost
2014-11-09 22:43     ` Ludovic Courtès
2014-11-10 13:18       ` Alex Kost
2014-11-10 23:29         ` Ludovic Courtès
2014-11-11 19:13           ` Alex Kost [this message]
2014-11-11 19:57             ` Ludovic Courtès
2014-11-12  6:56               ` Alex Kost
2014-11-12  9:55                 ` 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=877fz14kjx.fsf@gmail.com \
    --to=alezost@gmail.com \
    --cc=guix-devel@gnu.org \
    --cc=ludo@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.