unofficial mirror of guix-devel@gnu.org 
 help / color / mirror / code / Atom feed
* [PATCH 3/3] emacs: Add "Source" field to 'guix-info' buffers.
@ 2014-11-09  8:40 Alex Kost
  2014-11-09 17:45 ` Ludovic Courtès
  0 siblings, 1 reply; 10+ messages in thread
From: Alex Kost @ 2014-11-09  8:40 UTC (permalink / raw)
  To: guix-devel

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

This patch adds URL of a package source and 2 buttons ("Show" and
"Download") to an ‘info’ buffer (see the attached screenshot).

Pushing the "Show" button displays a store path of the package source.
Pushing the "Download" button has the same meaning as “guix build -S …”.


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

From c879b5520718726366a5afd83143315a16ab29a7 Mon Sep 17 00:00:00 2001
From: Alex Kost <alezost@gmail.com>
Date: Sun, 9 Nov 2014 11:03:39 +0300
Subject: [PATCH 3/3] 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-info-find-source-p): New variable.
  (guix-package-source): New button type.
  (guix-info-redisplay-entries, guix-package-info-insert-source-url,
  guix-package-info-show-source, guix-package-info-insert-source): New
  procedures.
* emacs/guix-base.el (guix-param-titles): Add 'source' parameter.
  (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  |  20 +++++++++
 emacs/guix-info.el  | 116 +++++++++++++++++++++++++++++++++++++++++++++++++---
 emacs/guix-main.scm |  49 ++++++++++++++++++++++
 3 files changed, 179 insertions(+), 6 deletions(-)

diff --git a/emacs/guix-base.el b/emacs/guix-base.el
index 784474e..a542a0c 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")
@@ -1008,6 +1010,24 @@ 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)))
+
+(defun guix-package-source-build-derivation (package-id)
+  "Build source derivation of a package PACKAGE-ID."
+  (when (or (not guix-operation-confirm)
+            (guix-operation-prompt))
+    (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)))))
+
 \f
 ;;; Pull
 
diff --git a/emacs/guix-info.el b/emacs/guix-info.el
index edb4450..bd56937 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.
@@ -190,6 +194,17 @@ LEVEL is 1 by default."
   "Insert `guix-info-indent' spaces LEVEL times (1 by default)."
   (insert (guix-info-get-indent level)))
 
+(defun guix-info-redisplay-entries (entries)
+  "Display entries in the current info buffer.
+Substitute `guix-entries' with ENTRIES."
+  (let ((point (point))
+        (window-start (window-start)))
+    (guix-set-buffer guix-profile entries guix-buffer-type
+                     guix-entry-type guix-search-type
+                     guix-search-vals t t)
+    (goto-char point)
+    (set-window-start nil window-start)))
+
 (defun guix-info-insert-entries (entries entry-type)
   "Display ENTRIES of ENTRY-TYPE in the current info buffer.
 ENTRIES should have a form of `guix-entries'."
@@ -334,7 +349,10 @@ VAL is a list, call the function on each element of this list."
   'face 'guix-info-file-path
   'help-echo "Find file"
   'action (lambda (btn)
-            (find-file (button-label btn))))
+            (let ((file (button-label btn)))
+              (if (file-exists-p file)
+                  (find-file file)
+                (message "File does not exist.")))))
 
 (define-button-type 'guix-url
   :supertype 'guix
@@ -652,6 +670,92 @@ ENTRY is an alist with package info."
   'guix-package-info-insert-output-path)
 
 \f
+;;; Inserting 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-find-source-p nil
+  "If non-nil, find a source file after pressing \"Show\" button.
+If nil, just display the source file path without finding."
+  :type 'boolean
+  :group 'guix-package-info)
+
+(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-find-source-p').
+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-get-key-val entry 'source-file)))
+    ;; Do not request a source file name if it has already been received.
+    (unless file
+      (setq 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-info-redisplay-entries entries)))
+    (if (file-exists-p file)
+        (if guix-package-info-find-source-p
+            (find-file file)
+          (message "The source store path is displayed."))
+      (message "The source does not exist in the store."))))
+
+(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* ((entry-id   (guix-get-key-val entry 'id))
+           (package-id (or (guix-get-key-val entry 'package-id)
+                           entry-id)))
+      (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)
+      (guix-info-insert-indent)
+      (guix-info-insert-action-button
+       "Download"
+       (lambda (btn)
+         (guix-package-source-build-derivation
+          (button-get btn 'package-id)))
+       "Build the source derivation (download the source if needed)"
+       'package-id package-id)
+      (let ((file (guix-get-key-val entry 'source-file)))
+        (when file
+          (guix-info-insert-val-simple file
+                                       #'guix-info-insert-file-path)))
+      (guix-info-insert-val-simple source
+                                   #'guix-package-info-insert-source-url))))
+
+\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


[-- Attachment #3: 2014-11-09_11:29:42.png --]
[-- Type: image/png, Size: 89154 bytes --]

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/3] emacs: Add "Source" field to 'guix-info' buffers.
  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
  0 siblings, 1 reply; 10+ messages in thread
From: Ludovic Courtès @ 2014-11-09 17:45 UTC (permalink / raw)
  To: Alex Kost; +Cc: guix-devel

Alex Kost <alezost@gmail.com> skribis:

> This patch adds URL of a package source and 2 buttons ("Show" and
> "Download") to an ‘info’ buffer (see the attached screenshot).
>
> Pushing the "Show" button displays a store path of the package source.
> Pushing the "Download" button has the same meaning as “guix build -S …”.

This is very cool!

I had in mind a slightly simpler user interface: just a “View source”
button.  The thing would build (package-source-derivation p),
effectively downloading it if it’s not already present, and opening it
in dired.

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.

WDYT?

Thanks,
Ludo’.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/3] emacs: Add "Source" field to 'guix-info' buffers.
  2014-11-09 17:45 ` Ludovic Courtès
@ 2014-11-09 18:48   ` Alex Kost
  2014-11-09 22:43     ` Ludovic Courtès
  0 siblings, 1 reply; 10+ messages in thread
From: Alex Kost @ 2014-11-09 18:48 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: guix-devel

Ludovic Courtès (2014-11-09 20:45 +0300) wrote:

> Alex Kost <alezost@gmail.com> skribis:
>
>> This patch adds URL of a package source and 2 buttons ("Show" and
>> "Download") to an ‘info’ buffer (see the attached screenshot).
>>
>> Pushing the "Show" button displays a store path of the package source.
>> Pushing the "Download" button has the same meaning as “guix build -S …”.
>
> This is very cool!
>
> I had in mind a slightly simpler user interface: just a “View source”
> button.  The thing would build (package-source-derivation p),
> effectively downloading it if it’s not already present, and opening it
> in dired.

I don't think opening in dired should be a default (although I made
‘guix-package-info-find-source-p’ variable +for you+ for this case)
because there may be a big compressed tarball that a user wouldn't like
to uncompress with emacs tar-mode.  I think displaying a link is the
preferable variant as a user can open the file or copy its path if he
wants.

Also you suggest to make “View source” synchronous: build a source
derivation, download it and open in dired.  Did I understand it right?

As this process may take a long time (for big sources or for git
checkouts if a user doesn't have a necessary git infrastructure), I
think it should be evaluated asynchronously in a REPL and it's not
possible (at least not easy) to define what value was returned by a REPL
command, so no way to define what file to open in dired.

Also what if I want to define whether there is a source in the store or
not?  (And I don't want to download it if it's not there.)  With your
variant of a single “View source” button it would not be possible, as
the source that doesn't exist in the store will be downloaded
unconditionally.

> 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?

> WDYT?

Pushing a “Download” button will always do something: it will run some
command in a REPL and will always display a store path in the end.  And
«Hey, USER, what did you expect from a “Download” button if the source
is already downloaded?»

But if you find it confusing what about the following variant: initially
there will be only “Show” button and when you press it, “Download”
button appears only if the source does not exist in the store.  After
a successful downloading, it disappears again.

-- 
Alex

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/3] emacs: Add "Source" field to 'guix-info' buffers.
  2014-11-09 18:48   ` Alex Kost
@ 2014-11-09 22:43     ` Ludovic Courtès
  2014-11-10 13:18       ` Alex Kost
  0 siblings, 1 reply; 10+ messages in thread
From: Ludovic Courtès @ 2014-11-09 22:43 UTC (permalink / raw)
  To: Alex Kost; +Cc: guix-devel

Alex Kost <alezost@gmail.com> skribis:

> Ludovic Courtès (2014-11-09 20:45 +0300) wrote:
>> I had in mind a slightly simpler user interface: just a “View source”
>> button.  The thing would build (package-source-derivation p),
>> effectively downloading it if it’s not already present, and opening it
>> in dired.
>
> I don't think opening in dired should be a default (although I made
> ‘guix-package-info-find-source-p’ variable +for you+ for this case)

:-)

> because there may be a big compressed tarball that a user wouldn't like
> to uncompress with emacs tar-mode.  I think displaying a link is the
> preferable variant as a user can open the file or copy its path if he
> wants.

Oh right, I didn’t think about big tarballs.

> Also you suggest to make “View source” synchronous: build a source
> derivation, download it and open in dired.  Did I understand it right?

Yes.

> As this process may take a long time (for big sources or for git
> checkouts if a user doesn't have a necessary git infrastructure), I
> think it should be evaluated asynchronously in a REPL and it's not
> possible (at least not easy) to define what value was returned by a REPL
> command, so no way to define what file to open in dired.

Oh, makes sense.  I completely overlooked “implementation details.”  :-)

> Also what if I want to define whether there is a source in the store or
> not?  (And I don't want to download it if it's not there.)

I don’t think it’s natural for a user to think in terms of downloads.
Personally, when I want to see the source of a package, I do:

  tar xf $(guix build -S foo)

and that’s it.  If it’s taking too long or something, I can still hit
C-c.  But typically, I don’t ask myself “is this already in the store?”.

> With your variant of a single “View source” button it would not be
> possible, as the source that doesn't exist in the store will be
> downloaded unconditionally.

Rather: the result of ‘package-source-derivation’ would be built
unconditionally; that entails a download if and only if the source is
not already in the store, otherwise nothing happens (which you probably
already know, but I just want to be clear.)

>> 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.

> Pushing a “Download” button will always do something: it will run some
> command in a REPL and will always display a store path in the end.  And
> «Hey, USER, what did you expect from a “Download” button if the source
> is already downloaded?»

Right, but we don’t need the user’s mind to carry part of the store’s
state: there’s already a database for that.  :-)

> But if you find it confusing what about the following variant: initially
> there will be only “Show” button and when you press it, “Download”
> button appears only if the source does not exist in the store.  After
> a successful downloading, it disappears again.

That would work, yes.

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?

If that’s fine with you, perhaps let’s just commit the patch as is, and
see in another patch whether “Download” can be made to disappear in safe
cases?

Thanks,
Ludo’.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/3] emacs: Add "Source" field to 'guix-info' buffers.
  2014-11-09 22:43     ` Ludovic Courtès
@ 2014-11-10 13:18       ` Alex Kost
  2014-11-10 23:29         ` Ludovic Courtès
  0 siblings, 1 reply; 10+ messages in thread
From: Alex Kost @ 2014-11-10 13:18 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: guix-devel

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

Ludovic Courtès (2014-11-10 01:43 +0300) wrote:

> Alex Kost <alezost@gmail.com> skribis:

[...]

>> Also what if I want to define whether there is a source in the store or
>> not?  (And I don't want to download it if it's not there.)
>
> I don’t think it’s natural for a user to think in terms of downloads.
> Personally, when I want to see the source of a package, I do:
>
>   tar xf $(guix build -S foo)

I think later we can provide some variable to choose if pushing a
"source file" button will open a tarball in a ‘tar-mode’ (a usual
'find-file' way) or will untar it in “/tmp” or something.

> and that’s it.  If it’s taking too long or something, I can still hit
> C-c.  But typically, I don’t ask myself “is this already in the store?”.

I typically ask myself this question :-)

>> With your variant of a single “View source” button it would not be
>> possible, as the source that doesn't exist in the store will be
>> downloaded unconditionally.
>
> Rather: the result of ‘package-source-derivation’ would be built
> unconditionally; that entails a download if and only if the source is
> not already in the store, otherwise nothing happens (which you probably
> already know, but I just want to be clear.)

Yes, I know, I meant that with your variant it's not possible to know if
you already have a source in the store or not: whenever you push “View
source” you will eventually have it there.

>>> 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?

>> Pushing a “Download” button will always do something: it will run some
>> command in a REPL and will always display a store path in the end.  And
>> «Hey, USER, what did you expect from a “Download” button if the source
>> is already downloaded?»
>
> Right, but we don’t need the user’s mind to carry part of the store’s
> state: there’s already a database for that.  :-)

OK.

>> But if you find it confusing what about the following variant: initially
>> there will be only “Show” button and when you press it, “Download”
>> button appears only if the source does not exist in the store.  After
>> a successful downloading, it disappears again.
>
> That would work, yes.
>
> 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?

> If that’s fine with you, perhaps let’s just commit the patch as is, and
> see in another patch whether “Download” can be made to disappear in safe
> cases?

I modified the patch (attached) to display “Show” and “Download” buttons
only when needed.  WDYT?

The patch may be applied to the current head (I have pushed a couple of
auxiliary commits).


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

From 71f8210da8f6d8524fd1d89b5a0cde5fcce1a25e 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-find-source-p, guix-package-info-download-buffer): New
  variables.
  (guix-package-info-show-source, guix-package-info-insert-source,
  guix-package-info-insert-source-url,
  guix-package-info-redisplay-after-download): New procedures.
* emacs/guix-base.el (guix-param-titles): Add 'source' parameter.
  (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  |  24 +++++++++++
 emacs/guix-info.el  | 115 +++++++++++++++++++++++++++++++++++++++++++++++++---
 emacs/guix-main.scm |  49 ++++++++++++++++++++++
 3 files changed, 183 insertions(+), 5 deletions(-)

diff --git a/emacs/guix-base.el b/emacs/guix-base.el
index a6e56dc..10d6909 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")
@@ -1035,6 +1037,28 @@ 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)
+  "Build source derivation of a package PACKAGE-ID."
+  (when (or (not guix-operation-confirm)
+            (guix-operation-prompt))
+    (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..9583b5e 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,107 @@ 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-find-source-p nil
+  "If non-nil, find a source file after pressing \"Show\" button.
+If nil, just display the source file path without finding."
+  :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-find-source-p').
+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-get-key-val entry 'source-file)))
+    ;; Do not request a source file name if it has already been received.
+    (unless file
+      (setq 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-find-source-p
+            (guix-find-file file)
+          (message "The source store path is displayed."))
+      (message "The source does not exist in the store."))))
+
+(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)
+             (setq guix-package-info-download-buffer (current-buffer))
+             (guix-package-source-build-derivation
+              (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


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/3] emacs: Add "Source" field to 'guix-info' buffers.
  2014-11-10 13:18       ` Alex Kost
@ 2014-11-10 23:29         ` Ludovic Courtès
  2014-11-11 19:13           ` Alex Kost
  0 siblings, 1 reply; 10+ messages in thread
From: Ludovic Courtès @ 2014-11-10 23:29 UTC (permalink / raw)
  To: Alex Kost; +Cc: guix-devel

Alex Kost <alezost@gmail.com> skribis:

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

[...]

>> I don’t think it’s natural for a user to think in terms of downloads.
>> Personally, when I want to see the source of a package, I do:
>>
>>   tar xf $(guix build -S foo)
>
> I think later we can provide some variable to choose if pushing a
> "source file" button will open a tarball in a ‘tar-mode’ (a usual
> 'find-file' way) or will untar it in “/tmp” or something.

OK.

>> and that’s it.  If it’s taking too long or something, I can still hit
>> C-c.  But typically, I don’t ask myself “is this already in the store?”.
>
> I typically ask myself this question :-)

OK, fair enough!

>>>> 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.

>> 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?

>> If that’s fine with you, perhaps let’s just commit the patch as is, and
>> see in another patch whether “Download” can be made to disappear in safe
>> cases?
>
> I modified the patch (attached) to display “Show” and “Download” buttons
> only when needed.  WDYT?

Sounds good!

> +(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?.)

Thanks,
Ludo’.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/3] emacs: Add "Source" field to 'guix-info' buffers.
  2014-11-10 23:29         ` Ludovic Courtès
@ 2014-11-11 19:13           ` Alex Kost
  2014-11-11 19:57             ` Ludovic Courtès
  0 siblings, 1 reply; 10+ messages in thread
From: Alex Kost @ 2014-11-11 19:13 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: guix-devel

[-- 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


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/3] emacs: Add "Source" field to 'guix-info' buffers.
  2014-11-11 19:13           ` Alex Kost
@ 2014-11-11 19:57             ` Ludovic Courtès
  2014-11-12  6:56               ` Alex Kost
  0 siblings, 1 reply; 10+ messages in thread
From: Ludovic Courtès @ 2014-11-11 19:57 UTC (permalink / raw)
  To: Alex Kost; +Cc: guix-devel

Alex Kost <alezost@gmail.com> skribis:

> 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?

Yep, sounds good!

>> 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.

It’s not really comparable, because GC is always something that can
happen concurrently (one may choose to run it from cron, for instance.)
But let’s ignore this possibility for the present case.  :-)

>>> +(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.

There’s also ‘svn-reference’ (not currently used), and possibly other
things.

The point is that this part will have to be updated anytime new origin
methods are added.

> 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.

I think we’re all set now, no?  :-)

Thanks,
Ludo’.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/3] emacs: Add "Source" field to 'guix-info' buffers.
  2014-11-11 19:57             ` Ludovic Courtès
@ 2014-11-12  6:56               ` Alex Kost
  2014-11-12  9:55                 ` Ludovic Courtès
  0 siblings, 1 reply; 10+ messages in thread
From: Alex Kost @ 2014-11-12  6:56 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: guix-devel

Ludovic Courtès (2014-11-11 22:57 +0300) wrote:

> Alex Kost <alezost@gmail.com> skribis:
>
>> Ludovic Courtès (2014-11-11 02:29 +0300) wrote:
[...]
>>> 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.
>
> It’s not really comparable, because GC is always something that can
> happen concurrently (one may choose to run it from cron, for instance.)
> But let’s ignore this possibility for the present case.  :-)

OK, thanks

>>>> +(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.
>
> There’s also ‘svn-reference’ (not currently used), and possibly other
> things.
>
> The point is that this part will have to be updated anytime new origin
> methods are added.

Yes, sure.  What about making “(else "Unknown source type")”?  And if
some package will use a new origin method, a “Source” field will display
“Unknown source type” string, so we'll not forget to update this thing.

>> 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.
>
> I think we’re all set now, no?  :-)

I think so.  May I commit with the above “Unknown source type” addition?

-- 
Alex

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/3] emacs: Add "Source" field to 'guix-info' buffers.
  2014-11-12  6:56               ` Alex Kost
@ 2014-11-12  9:55                 ` Ludovic Courtès
  0 siblings, 0 replies; 10+ messages in thread
From: Ludovic Courtès @ 2014-11-12  9:55 UTC (permalink / raw)
  To: Alex Kost; +Cc: guix-devel

Alex Kost <alezost@gmail.com> skribis:

> Ludovic Courtès (2014-11-11 22:57 +0300) wrote:

[...]

>>> 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.
>>
>> There’s also ‘svn-reference’ (not currently used), and possibly other
>> things.
>>
>> The point is that this part will have to be updated anytime new origin
>> methods are added.
>
> Yes, sure.  What about making “(else "Unknown source type")”?  And if
> some package will use a new origin method, a “Source” field will display
> “Unknown source type” string, so we'll not forget to update this thing.

Yes, makes sense.


[...]

>> I think we’re all set now, no?  :-)
>
> I think so.  May I commit with the above “Unknown source type” addition?

Yes, please.  Thank you!

Ludo’.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2014-11-12  9:55 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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
2014-11-11 19:57             ` Ludovic Courtès
2014-11-12  6:56               ` Alex Kost
2014-11-12  9:55                 ` Ludovic Courtès

Code repositories for project(s) associated with this public inbox

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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).