Commit f67aa2be authored by Ludovic Courtès's avatar Ludovic Courtès
Browse files

kernel: Add ";;guix search" magic.

* guix-jupyter-kernel.scm (search-inferior-packages)
(reply-search-results): New procedures.
(reply-execute-request): Handle ";;guix search".
(%magic-commands): Add "search".
* README.org (Searching for packages): New section.
parent 49da4998
......@@ -46,6 +46,23 @@ to Guix itself, so Guix must be installed on your machine and its build
daemon must up and running. Currently support for Linux unprivileged
user namespaces is also required (see below).
*** Searching for packages
Before you declare the software environment of your notebook, you might
want to search for packages so you can determine which ones to add to
the environment. You can do that by entering the following line in a
cell and evaluating it:
#+begin_src scheme
;;guix search PATTERN1 PATTERN2
#+end_src
… where PATTERN1 and PATTERN2 and words or regexps to search for.
The result is a (possibly empty) table showing the most relevant
packages for these patterns, including their name, version, and a
synopsis.
*** Environments
From there on, annotations, aka. /magics/, allow you to create and use
......
......@@ -29,6 +29,8 @@
(rnrs bytevectors)
(ice-9 match)
(sxml simple)
(texinfo)
(texinfo html)
(guix build syscalls)
(guix gexp)
(guix store)
......@@ -38,6 +40,7 @@
(guix inferior)
(guix monads)
(guix profiles)
((guix i18n) #:select (P_))
(gcrypt base16)
(gcrypt hash)
(jupyter messages)
......@@ -187,6 +190,95 @@ to CHANNELS in PROFILE."
,(channels->shtml instances)))
count)))
(define* (search-inferior-packages inferior patterns
#:key (max-results 20))
"Return the list of name/version/synopsis tuples for the most relevant
packages matching PATTERNS, a list of string (possibly regexps). Return at
most MAX-RESULTS elements."
;; Perform search in the inferior to reduce communication between the host
;; and the inferior, and to avoid allocating memory on both sides.
(inferior-eval
`(begin
(use-modules ((guix ui) #:select (package-relevance))
(ice-9 regex)
(ice-9 match)
(srfi srfi-1)
(srfi srfi-26))
(define regexps
(map (cut make-regexp <> regexp/icase) ',patterns))
(define (find-packages-by-description regexps)
;; XXX: Copied from (guix scripts package).
(let ((matches (fold-packages
(lambda (package result)
(if (package-superseded package)
result
(match (package-relevance package regexps)
((? zero?) result)
(score
(cons (cons package score) result)))))
'())))
(sort matches
(lambda (m1 m2)
(match m1
((package1 . score1)
(match m2
((package2 . score2)
(if (= score1 score2)
(string>? (package-full-name package1)
(package-full-name package2))
(> score1 score2))))))))))
(let ((lst (find-packages-by-description regexps)))
(map (match-lambda
((package . score)
(list (package-name package) (package-version package)
(package-synopsis package))))
(if (> (length lst) ,max-results)
(take lst ,max-results)
lst))))
inferior))
(define* (reply-search-results kernel message inferior patterns
#:key (count 0))
"Send to KERNEL a reply to MESSAGE showing search results for PATTERNS, a
list of regular expressions (strings)."
(define (url package)
(string-append "https://hpc.guix.info/package/" package))
(define (ref package)
`(a (@ (href ,(url package))) ,package))
(define (synopsis->shtml synopsis)
;; 'texi-fragment->stexi' uses 'call-with-input-string', so make sure
;; those string ports are Unicode-capable.
(with-fluids ((%default-port-encoding "UTF-8"))
(and=> synopsis
(compose stexi->shtml texi-fragment->stexi P_))))
(catch 'regular-expression-syntax
(lambda ()
;; Ensure PATTERNS are valid regexps.
(for-each (cut make-regexp <> regexp/icase) patterns)
(let ((lst (search-inferior-packages inferior patterns)))
(reply-html kernel message
(sxml->html-string
`(p (table
,@(map (match-lambda
((name version synopsis)
`(tr (td ,(ref name)) (td ,version)
(td ,(synopsis->shtml synopsis)))))
lst))))
count)))
(lambda (key . args)
(reply-html kernel message
(sxml->html-string
`(div (@ (class "ansi-red-fg"))
"Invalid regular expression."))
count))))
(define* (reply-channel-description kernel message inferior
#:key (count 0))
"Reply to MESSAGE, which comes from KERNEL, with a description of the
......@@ -419,6 +511,12 @@ stripped."
(send-message proxy (execute-request-sans-magic message))
(let ((state (increment-execution-count state)))
(set-proxy-state-default-environment state name)))))
((";;guix" "search" patterns ...)
(let* ((state (ensure-proxy-state-inferior state))
(inferior (proxy-state-inferior state)))
(reply-search-results kernel message inferior patterns
#:count count)
(increment-execution-count state)))
((";;guix" "describe")
(let* ((state (ensure-proxy-state-inferior state))
(inferior (proxy-state-inferior state)))
......@@ -495,7 +593,7 @@ python-ipykernel"))
(define %magic-commands
;; The ";;guix" magic commands.
'("describe" "download" "environment" "pin"))
'("describe" "download" "environment" "pin" "search"))
(define (reply-complete-request kernel kind message state)
"Reply to a \"complete_request\" message--i.e., a completion request.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment