ui: Improve pager selection logic when less is not installed.
* guix/ui.scm (find-available-pager): New procedure.
(call-with-paginated-output-port): Use it.
* guix/utils.scm (call-with-environment-variables): Allow clearing of
specified environment variables.
* tests/ui.scm (make-empty-file, assert-equals-find-available-pager):
New procedures.
("find-available-pager, GUIX_PAGER takes precedence")
("find-available-pager, PAGER takes precedence")
("find-available-pager, 'less' takes precedence")
("find-available-pager, 'more' takes precedence")
("find-available-pager, no pager"): New tests.
Co-authored-by: Ludovic Courtès <ludo@gnu.org>
			
			
This commit is contained in:
		
							parent
							
								
									a88de093fb
								
							
						
					
					
						commit
						c8803d89fe
					
				
					 3 changed files with 82 additions and 3 deletions
				
			
		
							
								
								
									
										14
									
								
								guix/ui.scm
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								guix/ui.scm
									
										
									
									
									
								
							| 
						 | 
					@ -17,6 +17,7 @@
 | 
				
			||||||
;;; Copyright © 2020 Arun Isaac <arunisaac@systemreboot.net>
 | 
					;;; Copyright © 2020 Arun Isaac <arunisaac@systemreboot.net>
 | 
				
			||||||
;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
 | 
					;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
 | 
				
			||||||
;;; Copyright © 2018 Steve Sprang <scs@stevesprang.com>
 | 
					;;; Copyright © 2018 Steve Sprang <scs@stevesprang.com>
 | 
				
			||||||
 | 
					;;; Copyright © 2022 Taiju HIGASHI <higashi@taiju.info>
 | 
				
			||||||
;;;
 | 
					;;;
 | 
				
			||||||
;;; This file is part of GNU Guix.
 | 
					;;; This file is part of GNU Guix.
 | 
				
			||||||
;;;
 | 
					;;;
 | 
				
			||||||
| 
						 | 
					@ -1672,11 +1673,18 @@ return the underlying port.  Otherwise return #f."
 | 
				
			||||||
    (_
 | 
					    (_
 | 
				
			||||||
     #f)))
 | 
					     #f)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (find-available-pager)
 | 
				
			||||||
 | 
					  "Return the program name of an available pager or the empty string if none is
 | 
				
			||||||
 | 
					available."
 | 
				
			||||||
 | 
					  (or (getenv "GUIX_PAGER")
 | 
				
			||||||
 | 
					      (getenv "PAGER")
 | 
				
			||||||
 | 
					      (which "less")
 | 
				
			||||||
 | 
					      (which "more")
 | 
				
			||||||
 | 
					      ""))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(define* (call-with-paginated-output-port proc
 | 
					(define* (call-with-paginated-output-port proc
 | 
				
			||||||
                                          #:key (less-options "FrX"))
 | 
					                                          #:key (less-options "FrX"))
 | 
				
			||||||
  (let ((pager-command-line (or (getenv "GUIX_PAGER")
 | 
					  (let ((pager-command-line (find-available-pager)))
 | 
				
			||||||
                                (getenv "PAGER")
 | 
					 | 
				
			||||||
                                "less")))
 | 
					 | 
				
			||||||
    ;; Setting PAGER to the empty string conventionally disables paging.
 | 
					    ;; Setting PAGER to the empty string conventionally disables paging.
 | 
				
			||||||
    (if (and (not (string-null? pager-command-line))
 | 
					    (if (and (not (string-null? pager-command-line))
 | 
				
			||||||
             (isatty?* (current-output-port)))
 | 
					             (isatty?* (current-output-port)))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,7 @@
 | 
				
			||||||
;;; Copyright © 2021 Chris Marusich <cmmarusich@gmail.com>
 | 
					;;; Copyright © 2021 Chris Marusich <cmmarusich@gmail.com>
 | 
				
			||||||
;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be>
 | 
					;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be>
 | 
				
			||||||
;;; Copyright © 2018 Steve Sprang <scs@stevesprang.com>
 | 
					;;; Copyright © 2018 Steve Sprang <scs@stevesprang.com>
 | 
				
			||||||
 | 
					;;; Copyright © 2022 Taiju HIGASHI <higashi@taiju.info>
 | 
				
			||||||
;;;
 | 
					;;;
 | 
				
			||||||
;;; This file is part of GNU Guix.
 | 
					;;; This file is part of GNU Guix.
 | 
				
			||||||
;;;
 | 
					;;;
 | 
				
			||||||
| 
						 | 
					@ -158,6 +159,8 @@
 | 
				
			||||||
    (dynamic-wind
 | 
					    (dynamic-wind
 | 
				
			||||||
      (lambda ()
 | 
					      (lambda ()
 | 
				
			||||||
        (for-each (match-lambda
 | 
					        (for-each (match-lambda
 | 
				
			||||||
 | 
					                    ((variable #false)
 | 
				
			||||||
 | 
					                     (unsetenv variable))
 | 
				
			||||||
                    ((variable value)
 | 
					                    ((variable value)
 | 
				
			||||||
                     (setenv variable value)))
 | 
					                     (setenv variable value)))
 | 
				
			||||||
                  variables))
 | 
					                  variables))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										68
									
								
								tests/ui.scm
									
										
									
									
									
								
							
							
						
						
									
										68
									
								
								tests/ui.scm
									
										
									
									
									
								
							| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
;;; GNU Guix --- Functional package management for GNU
 | 
					;;; GNU Guix --- Functional package management for GNU
 | 
				
			||||||
;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
 | 
					;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
 | 
				
			||||||
 | 
					;;; Copyright © 2022 Taiju HIGASHI <higashi@taiju.info>
 | 
				
			||||||
;;;
 | 
					;;;
 | 
				
			||||||
;;; This file is part of GNU Guix.
 | 
					;;; This file is part of GNU Guix.
 | 
				
			||||||
;;;
 | 
					;;;
 | 
				
			||||||
| 
						 | 
					@ -24,6 +25,7 @@
 | 
				
			||||||
  #:use-module (guix derivations)
 | 
					  #:use-module (guix derivations)
 | 
				
			||||||
  #:use-module ((gnu packages) #:select (specification->package))
 | 
					  #:use-module ((gnu packages) #:select (specification->package))
 | 
				
			||||||
  #:use-module (guix tests)
 | 
					  #:use-module (guix tests)
 | 
				
			||||||
 | 
					  #:use-module (guix utils)
 | 
				
			||||||
  #:use-module (srfi srfi-1)
 | 
					  #:use-module (srfi srfi-1)
 | 
				
			||||||
  #:use-module (srfi srfi-11)
 | 
					  #:use-module (srfi srfi-11)
 | 
				
			||||||
  #:use-module (srfi srfi-19)
 | 
					  #:use-module (srfi srfi-19)
 | 
				
			||||||
| 
						 | 
					@ -292,4 +294,70 @@ Second line" 24))
 | 
				
			||||||
         (>0 (package-relevance libb2
 | 
					         (>0 (package-relevance libb2
 | 
				
			||||||
                                (map rx '("crypto" "library")))))))
 | 
					                                (map rx '("crypto" "library")))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (make-empty-file directory file)
 | 
				
			||||||
 | 
					  ;; Create FILE in DIRECTORY.
 | 
				
			||||||
 | 
					  (close-port (open-output-file (in-vicinity directory file))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (assert-equals-find-available-pager expected)
 | 
				
			||||||
 | 
					  ;; Use 'with-paginated-output-port' and return true if it invoked EXPECTED.
 | 
				
			||||||
 | 
					  (define used-command "")
 | 
				
			||||||
 | 
					  (mock ((ice-9 popen) open-pipe*
 | 
				
			||||||
 | 
					         (lambda (mode command . args)
 | 
				
			||||||
 | 
					           (unless (string-null? used-command)
 | 
				
			||||||
 | 
					             (error "open-pipe* should only be called once"))
 | 
				
			||||||
 | 
					           (set! used-command command)
 | 
				
			||||||
 | 
					           (%make-void-port "")))
 | 
				
			||||||
 | 
					        (mock ((ice-9 popen) close-pipe (const 'ok))
 | 
				
			||||||
 | 
					              (mock ((guix colors) isatty?* (const #t))
 | 
				
			||||||
 | 
					                    (with-paginated-output-port port 'ok)
 | 
				
			||||||
 | 
					                    (string=? expected used-command)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(test-assert "find-available-pager, GUIX_PAGER takes precedence"
 | 
				
			||||||
 | 
					  (call-with-temporary-directory
 | 
				
			||||||
 | 
					   (lambda (dir)
 | 
				
			||||||
 | 
					     (with-environment-variables `(("PATH" ,dir)
 | 
				
			||||||
 | 
					                                   ("GUIX_PAGER" "guix-pager")
 | 
				
			||||||
 | 
					                                   ("PAGER" "pager"))
 | 
				
			||||||
 | 
					       (make-empty-file dir "less")
 | 
				
			||||||
 | 
					       (make-empty-file dir "more")
 | 
				
			||||||
 | 
					       (assert-equals-find-available-pager "guix-pager")))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(test-assert "find-available-pager, PAGER takes precedence"
 | 
				
			||||||
 | 
					  (call-with-temporary-directory
 | 
				
			||||||
 | 
					   (lambda (dir)
 | 
				
			||||||
 | 
					     (with-environment-variables `(("PATH" ,dir)
 | 
				
			||||||
 | 
					                                   ("GUIX_PAGER" #false)
 | 
				
			||||||
 | 
					                                   ("PAGER" "pager"))
 | 
				
			||||||
 | 
					       (make-empty-file dir "less")
 | 
				
			||||||
 | 
					       (make-empty-file dir "more")
 | 
				
			||||||
 | 
					       (assert-equals-find-available-pager "pager")))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(test-assert "find-available-pager, 'less' takes precedence"
 | 
				
			||||||
 | 
					  (call-with-temporary-directory
 | 
				
			||||||
 | 
					   (lambda (dir)
 | 
				
			||||||
 | 
					     (with-environment-variables `(("PATH" ,dir)
 | 
				
			||||||
 | 
					                                   ("GUIX_PAGER" #false)
 | 
				
			||||||
 | 
					                                   ("PAGER" #false))
 | 
				
			||||||
 | 
					       (make-empty-file dir "less")
 | 
				
			||||||
 | 
					       (make-empty-file dir "more")
 | 
				
			||||||
 | 
					       (assert-equals-find-available-pager (in-vicinity dir "less"))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(test-assert "find-available-pager, 'more' takes precedence"
 | 
				
			||||||
 | 
					  (call-with-temporary-directory
 | 
				
			||||||
 | 
					   (lambda (dir)
 | 
				
			||||||
 | 
					     (with-environment-variables `(("PATH" ,dir)
 | 
				
			||||||
 | 
					                                   ("GUIX_PAGER" #false)
 | 
				
			||||||
 | 
					                                   ("PAGER" #false))
 | 
				
			||||||
 | 
					       (make-empty-file dir "more")
 | 
				
			||||||
 | 
					       (assert-equals-find-available-pager (in-vicinity dir "more"))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(test-assert "find-available-pager, no pager"
 | 
				
			||||||
 | 
					  (call-with-temporary-directory
 | 
				
			||||||
 | 
					   (lambda (dir)
 | 
				
			||||||
 | 
					     (with-environment-variables `(("PATH" ,dir)
 | 
				
			||||||
 | 
					                                   ("GUIX_PAGER" #false)
 | 
				
			||||||
 | 
					                                   ("PAGER" #false))
 | 
				
			||||||
 | 
					       (assert-equals-find-available-pager "")))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(test-end "ui")
 | 
					(test-end "ui")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Reference in a new issue