5e6b2fd0a0e1

Add --one to pick
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Wed, 02 Sep 2020 12:12:39 -0400
parents 411d31e2360b
children 8b5b2b90ac77 1fa04416fcdb
branches/tags (none)
files lisp/pick.lisp

Changes

--- a/lisp/pick.lisp	Wed Sep 02 11:45:44 2020 -0400
+++ b/lisp/pick.lisp	Wed Sep 02 12:12:39 2020 -0400
@@ -32,20 +32,33 @@
               ((matchesp line '("y" "yes")) (return t))
               ((matchesp line '("n" "no" "")) (return nil)))))
 
-(defun filter (choices)
+(defun filter-many (choices)
   (loop
     :with width = (1+ (reduce #'max choices :key #'length :initial-value 0))
     :for choice :in choices
     :when (prompt "~A~vA[yN] " choice (- width (length choice)) #\space)
     :collect choice))
 
+(defun filter-one (choices)
+  (loop :for choice :in choices
+        :for i :from 0
+        :do (format *interactive-output* "~36R) ~A~%" i choice)
+        :collect choice)
+  (let ((i (parse-integer (read-line *interactive-input*) :radix 36)))
+    (if (or (minusp i) (>= i (length choices)))
+      (error "Bad choice ~d" i)
+      (list (elt choices i)))))
+
 (defun output (results)
   (loop :for (r . remaining) :on results
         :do (write-string r)
         :when remaining :do (write-string *separator*)))
 
-(defun run (choices)
-  (output (filter choices)))
+(defun run-many (choices)
+  (output (filter-many choices)))
+
+(defun run-one (choices)
+  (output (filter-one choices)))
 
 
 ;;;; User Interface -----------------------------------------------------------
@@ -86,6 +99,22 @@
     :short #\0
     :reduce (constantly (string #\nul))))
 
+(defparameter *option-one*
+  (adopt:make-option 'one
+    :help "Pick a single line instead of picking one-by-one."
+    :long "one"
+    :short #\o
+    :reduce (constantly t)
+    :initial-value nil))
+
+(defparameter *option-many*
+  (adopt:make-option 'many
+    :result-key 'one
+    :help "Pick multiple lines, asking one-by-one (the default)."
+    :long "many"
+    :short #\O
+    :reduce (constantly nil)))
+
 
 (adopt:define-string *help-text*
   "pick displays its arguments one-by-one on standard error and prompts you ~
@@ -97,6 +126,11 @@
    arguments are present prevents something like 'pick `ls -1 | grep foo`' from ~
    silently hanging forever if no files match.~@
    ~@
+   Using the --one argument changes the behaviour of pick.  Instead of ~
+   picking several lines from the input by asking one-by-one, all of the lines ~
+   are presented at once and the user is prompted to pick one of them with ~
+   a prefix.
+   ~@
    This version was inspired by the pick program described in 'The UNIX ~
    Programming Environment'.")
 
@@ -110,7 +144,9 @@
     :contents (list *option-help*
                     *option-version*
                     *option-separator*
-                    *option-null*)))
+                    *option-null*
+                    *option-one*
+                    *option-many*)))
 
 
 (defun toplevel ()
@@ -120,11 +156,15 @@
               ((gethash 'version options) (write-line *version*) (adopt:exit)))
         (with-open-file (*interactive-input* "/dev/tty" :direction :input)
           (let ((*separator* (gethash 'separator options))
-                (*interactive-output* *error-output*))
-            (run (mapcan (lambda (arg)
-                           (if (string= "-" arg)
-                             (read-lines *standard-input*)
-                             (list arg)))
-                         arguments)))))
+                (*interactive-output* *error-output*)
+                (arguments (mapcan (lambda (arg)
+                                     (if (string= "-" arg)
+                                       (read-lines *standard-input*)
+                                       (list arg)))
+                                   arguments)))
+            (funcall (if (gethash 'one options)
+                       #'run-one
+                       #'run-many)
+                     arguments))))
     (error (c) (adopt:print-error-and-exit c))))