aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build-app.lisp62
-rw-r--r--src/lib.lisp191
2 files changed, 100 insertions, 153 deletions
diff --git a/build-app.lisp b/build-app.lisp
index 3511a8c..a3ef6fc 100644
--- a/build-app.lisp
+++ b/build-app.lisp
@@ -62,8 +62,6 @@ you run the oneliner, all positional variables appear first.
;;; CLON SYNOPSIS DEFINITION
(defsynopsis (:postfix "[ARGUMENTS ...]")
-
-
(group (:header "SEARCH OPTIONS")
(text :contents "By default, ARGUMENTS are interpeted as search terms for oneliners. For example:")
(text :contents "$ ol grep awk # search for oneliners involving both grep and awk")
@@ -82,15 +80,11 @@ you run the oneliner, all positional variables appear first.
(flag :long-name "newest"
:description "Return newest oneliners."))
(text :contents " ")
-
-
(group (:header "EXECUTION OPTIONS")
(text :contents "Several options override the default interpretation of ARGUMENTS.")
(text :contents "Execution options interpret the first argument as the identifier of a oneliner: ")
(text :contents "$ ol <EXECUTION OPTION> <NAME or ID> [MORE ARGUMENTS...]")
(text :contents " ")
-
-
(flag :long-name "run"
:description "Executes a oneliner by NAME or ID. See also help topic 'variables'.")
(flag :long-name "clip"
@@ -111,11 +105,9 @@ you run the oneliner, all positional variables appear first.
:description "View information about a contributor. The first argument is a contributor handle.")
(flag :long-name "help"
:description "Print help for a topic. Topics: wiki, account, invites, variables"))
-
(group (:header "Advanced Options" :hidden t)
(flag :long-name "clear-cache"
:description "Clears all cached search results from your system."))
-
(group (:header "Variables" :hidden t)
(text :contents +oneliners-variables-help-text+))
(group (:header "Wiki" :hidden t)
@@ -166,6 +158,14 @@ than the users."
;;; MAIN ENTRY POINT
+(defun prepare-oneliner-arguments (arguments)
+ "Takes a list of arguments, as gathered by (REMAINDER), and returns
+ a list that looks like (ID-OR-NAME . ARGS) where ID-OR-NAME is
+ either an integer or a string."
+ (a:if-let (id (parse-integer (first arguments) :junk-allowed t))
+ (cons id (rest arguments))
+ arguments))
+
(defun main ()
"Entry point for our standalone application."
(make-context)
@@ -176,18 +176,21 @@ than the users."
(let ((arguments (remainder)))
(cond
((getopt :long-name "whois")
- (assert (first arguments))
+ (assert (first arguments) () "--whois requires an argument, a user handle.")
(cli::show-contributor (first arguments)))
((getopt :long-name "redeem")
+ (assert (= 3 (length arguments)) () "--redeem requires exatly three arguments.")
(destructuring-bind (token name pass) arguments
(cli::redeem-invite token name pass)))
((getopt :long-name "login")
+ (assert (= 2 (length arguments)) () "--login requires exactly two arguments.")
(destructuring-bind (user pass) arguments
(cli::login user pass)))
((getopt :long-name "change-password")
+ (assert (= 3 (length arguments)) () "--change-password requires exactly three arguments." )
(destructuring-bind (current new repeated) arguments
(cli::change-pw current new repeated)))
@@ -204,40 +207,43 @@ than the users."
(cli::add-new-oneliner))
((getopt :long-name "all-flagged")
+ (format t "WARNING: --all-flagged is likely to soon change its behavior~%.")
(cli::all-flagged-oneliners (getopt :long-name "limit")))
((getopt :long-name "newest")
+ (format t "WARNING: --newest is likely to soon change its behavior~%")
(cli::newest-oneliners (getopt :long-name "limit")))
((getopt :long-name "clear-cache")
(cli::wipe-cache))
(arguments
- ;; when the first argument is a number, try run a oneliner
- (a:when-let (hist-number (parse-integer (first arguments) :junk-allowed t))
+ (destructuring-bind (id-or-name . args) (prepare-oneliner-arguments arguments)
(cond
((getopt :long-name "flag")
- (cli::flag-item hist-number (getopt :long-name "id")))
+ (cli::flag-item id-or-name ))
((getopt :long-name "unflag")
- (cli::unflag-item hist-number (getopt :long-name "id")))
+ (cli::unflag-item id-or-name))
((getopt :long-name "lock")
- (cli::lock-item hist-number (getopt :long-name "id")))
+ (cli::lock-item id-or-name ))
((getopt :long-name "unlock")
- (cli::unlock-item hist-number (getopt :long-name "id")))
+ (cli::unlock-item id-or-name ))
((getopt :long-name "edit")
- (cli::edit-item hist-number (getopt :long-name "id")))
- ((getopt :long-name "explain")
- (cli::print-item-explanation hist-number (getopt :long-name "id")))
- (t
- (cli::run-item hist-number (rest arguments)
- :by-id (getopt :long-name "id")
- :force-clip (getopt :long-name "clip")
- :timeout (getopt :long-name "timeout"))))
- (uiop:quit))
- ;; otherwise search for oneliners
- (cli::search-for-oneliners arguments
- (getopt :long-name "limit")
- (getopt :long-name "not-flagged")))
+ (cli::edit-item id-or-name ))
+ ((getopt :long-name "info")
+ (cli::print-item-explanation id-or-name))
+ ((getopt :long-name "clip")
+ (cli::run-item id-or-name (rest arguments)
+ :force-clip t
+ :timeout (getopt :long-name "timeout")))
+ ((getopt :long-name "run")
+ (cli::run-item id-or-name (rest arguments)
+ :timeout (getopt :long-name "timeout")))
+ (t
+ (cli::search-for-oneliners arguments
+ (getopt :long-name "limit")
+ (getopt :long-name "not-flagged")))))
+ (uiop:quit))
(t
(help)))
(uiop:quit))
diff --git a/src/lib.lisp b/src/lib.lisp
index 070e021..2ade79b 100644
--- a/src/lib.lisp
+++ b/src/lib.lisp
@@ -69,29 +69,26 @@
(defun config-file ()
(merge-pathnames ".config/oneliners.config" (user-homedir-pathname)))
-(defun last-search-file ()
- (merge-pathnames ".last_oneliners_search" (user-homedir-pathname)))
-
(defun cached-oneliners-file ()
(merge-pathnames ".cached_oneliners" (user-homedir-pathname)))
(defun wipe-cache ()
- (uiop:delete-file-if-exists (cached-oneliners-file))
- (uiop:delete-file-if-exists (last-search-file )))
+ (uiop:delete-file-if-exists (cached-oneliners-file)))
-(defun merge-into-cache (ols)
+(defun merge-into-cache (new-ols)
(if (uiop:file-exists-p (cached-oneliners-file))
- (let ((cached (with-open-file (input (cached-oneliners-file)) (read input))))
+ (let ((cached-ols (with-open-file (input (cached-oneliners-file)) (read input))))
(with-open-file (out (cached-oneliners-file) :direction :output :if-exists :supersede)
- (print (nconc
- ols
- (remove-if
- (lambda (old)
- (member (getf old :id) ols :test 'equal :key (lambda (x) (getf x :id))))
- cached))
- out)))
- (with-open-file (out (cached-oneliners-file) :direction :output :if-exists :supersede)
- (print ols out))))
+ (print
+ (nconc
+ new-ols
+ (remove-if
+ (lambda (old)
+ (find (getf old :id) new-ols :key (lambda (x) (getf x :id))))
+ cached-ols))
+ out)))
+ (with-open-file (out (cached-oneliners-file) :direction :output)
+ (print new-ols out))))
;;; UTILITIES
(defun make-temp-file-name ()
@@ -145,70 +142,69 @@ the directories that appear in the value of that variable."
;; unregisters the hook.
(rl:register-hook :pre-input nil)))
-(defun cached-result (n &optional idp)
- (if idp
- (when (uiop:file-exists-p (cached-oneliners-file))
- (let ((contents (with-open-file (input (cached-oneliners-file)) (read input))))
- (find n contents :key (lambda (x) (getf x :id)))))
- (when (uiop:file-exists-p (last-search-file))
- (let ((contents (with-open-file (input (last-search-file)) (read input))))
- (nth n contents)))))
-
-(defmacro with-cached-result ((olvar n &optional idp) &body body)
- (a:with-gensyms (nvar idpvar)
- `(let ((,nvar ,n)
- (,idpvar ,idp))
- (assert (plusp ,nvar) () "Item number must be 1 or greater")
- (a:if-let (,olvar (if ,idpvar (cached-result ,nvar t) (cached-result (1- ,nvar))))
+(defun the-oneliner (name-or-id)
+ "Get the oneliner with name-or-id. Try to fetch from local cache,
+and, failing that, try to fetch from configured server."
+ (a:if-let ((ol (cached-result name-or-id)))
+ ol
+ (progn
+ (ensure-config)
+ (a:when-let (ol
+ (api:request-with (:host (host))
+ (jonathan:parse
+ (api:get--oneliner-entry name-or-id))))
+ (merge-into-cache (list ol))
+ ol))))
+
+(defun cached-result (n)
+ (when (uiop:file-exists-p (cached-oneliners-file))
+ (let ((contents (with-open-file (input (cached-oneliners-file)) (read input))))
+ (etypecase n
+ (integer
+ (find n contents :key (lambda (x) (getf x :id))))
+ (string
+ (find n contents :key (lambda (x) (getf x :name)) :test #'equal))))))
+
+(defmacro with-oneliner ((var name-or-id) &body body)
+ (assert (symbolp var))
+ (let ((nvar (gensym)))
+ `(let ((,nvar ,name-or-id))
+ (a:if-let (,var (the-oneliner ,nvar))
(progn ,@body)
(format t "Could not find the oneliner specified by ~a~%" ,nvar)))))
-(defun print-item-explanation (number &optional idp)
- (let ((found nil))
- (with-cached-result (ol number idp)
- (setf found t)
- (when (getf ol :explanation)
- (princ #\newline)
- (princ (getf ol :explanation))))
- (when (and idp (not found))
- (format t "Trying to fetch oneliner with id ~a from the wiki.~%" number)
- (ensure-config)
- (a:if-let ((ol
- (api:request-with (:host (host))
- (jonathan:parse
- (api::get--oneliner-entry number)))))
- (progn
- (when (getf ol :explanation)
- (princ #\newline)
- (princ (getf ol :explanation)))
- (merge-into-cache (list ol)))
- (format t "Still couldn't find it. Are you connected?")))))
+(defun print-item-explanation (name-or-number)
+ (with-oneliner (ol name-or-number)
+ (print-oneliner-result-for-user ol)
+ (when (getf ol :explanation)
+ (princ #\newline)
+ (princ (getf ol :explanation)))))
;;; API REQUEST FUNCTIONS
-(defun flag-item (item-number &optional idp)
- (with-cached-result (ol item-number idp)
+(defun flag-item (ident)
+ (with-oneliner (ol ident)
(ensure-config)
(api:request-with
(:host (host))
(api:put--oneliner-entry-flag (getf ol :id) :token (api-token) :value "true"))))
-(defun unflag-item (item-number &optional idp)
- (with-cached-result (ol item-number idp)
+(defun unflag-item (item-number)
+ (with-oneliner (ol item-number)
(ensure-config)
(api:request-with
(:host (host))
(api:put--oneliner-entry-flag (getf ol :id) :token (api-token) :value "false"))))
-(defun lock-item (item-number &optional idp)
- (with-cached-result (ol item-number idp)
+(defun lock-item (item-number)
+ (with-oneliner (ol item-number)
(ensure-config)
(api:request-with
(:host (host))
(api:put--oneliner-oneliner-locked (getf ol :id) :token (api-token) :value "true"))))
-(defun unlock-item (item-number &optional idp)
- (with-cached-result (ol item-number idp)
+(defun unlock-item (item-number)
+ (with-oneliner (ol item-number)
(ensure-config)
(api:request-with
(:host (host))
@@ -262,22 +258,10 @@ the directories that appear in the value of that variable."
(handle-run-oneliner oneliner (or force-clip (equalp runstyle "manual"))))))
-(defun run-item (item-number args &key by-id force-clip (timeout nil timeout-p))
- (let ((*ol-output-timeout* (if timeout-p timeout *ol-output-timeout*))
- (found nil))
- (with-cached-result (ol item-number by-id)
- (setf found t)
- (bind-vars-and-run-oneliner ol args force-clip))
- (when (and by-id (not found))
- (format t "Trying to fetch oneliner with id ~a from the wiki.~%" item-number)
- (ensure-config)
- (a:if-let ((ol
- (api:request-with (:host (host))
- (jonathan:parse
- (api::get--oneliner-entry item-number)))))
- (progn (bind-vars-and-run-oneliner ol args force-clip)
- (merge-into-cache (list ol)))
- (format t "Still couldn't find it. Are you connected?")))))
+(defun run-item (ident args &key force-clip (timeout nil timeout-p))
+ (with-oneliner (ol ident)
+ (let ((*ol-output-timeout* (if timeout-p timeout *ol-output-timeout*)))
+ (bind-vars-and-run-oneliner ol args force-clip))))
(defun valid-oneliner-string-p (string)
(and (not (find #\newline string))
@@ -289,28 +273,6 @@ the directories that appear in the value of that variable."
(defun valid-runstyle-p (string)
(member string '("auto" "manual") :test 'equalp))
-(defun aliases ()
- (getf *config* :aliases))
-
-(defun (setf aliases) (newval)
- (setf (getf *config* :aliases) newval))
-
-(defun alias-item (item alias)
- (with-cached-result (ol item)
- (ensure-config)
- (a:if-let (found (assoc alias (aliases)))
- (setf (cdr found) ol)
- (push (cons alias ol) (aliases)))
- (write-config-to-disk)))
-
-(defun lookup-alias (alias)
- (cdr (assoc alias (aliases))))
-
-(defun run-alias (alias args &optional force-clip)
- (ensure-config)
- (a:when-let (ol (lookup-alias alias))
- (bind-vars-and-run-oneliner ol args force-clip)))
-
(defun add-new-oneliner ()
(ensure-config)
(assert (api-token) () "Cannot add a oneliner without an api token.")
@@ -352,10 +314,10 @@ the directories that appear in the value of that variable."
(api:post--oneliner :token (api-token))
(format t "Added~%"))))
-(defun edit-item (n &optional idp)
- (ensure-config)
- (assert (api-token) () "Cannot edit a oneliner without an api token.")
- (with-cached-result (ol n idp)
+(defun edit-item (ident)
+ (with-oneliner (ol ident)
+ (ensure-config)
+ (assert (api-token) () "Cannot edit a oneliner without an api token.")
(let* ((oneliner
(prompt "Oneliner: "
:expect 'valid-oneliner-string-p
@@ -400,9 +362,7 @@ the directories that appear in the value of that variable."
new-item)
:content-type "application/json")
(api:patch--oneliner-entry-edit (getf ol :id) :token (api-token))
- (if idp
- (update-history-item n new-item)
- (update-cached-item new-item))
+ (update-cached-item new-item)
(format t "OK~%"))))))
(defun request-invite-code ()
@@ -499,26 +459,8 @@ the directories that appear in the value of that variable."
(api:delete--access-access (api-token) :token (api-token))
(format t "You were logged out~%")))
-(defun update-history-item (n item)
- (when (uiop:file-exists-p (last-search-file))
- (let* ((results (with-open-file (input (last-search-file)) (read input)))
- (ol (nth (1- n) results)))
- (when ol
- (setf (nth (1- n) results) (append item ol))
- (cache-search-results-to-last-search-file results))))
- (merge-into-cache (list item)))
-
(defun update-cached-item (item)
- (let* ((history (with-open-file (input (last-search-file)) (read input)))
- (pos (position (getf item :id) history :key (lambda (x) (getf item :id)))))
- (if pos
- (update-history-item (1+ pos) item)
- (merge-into-cache (list item)))))
-
-(defun cache-search-results-to-last-search-file (results)
- (with-open-file (output (last-search-file) :direction :output :if-exists :supersede)
- (print results output))
- (merge-into-cache results))
+ (merge-into-cache (list item)))
(defvar *term-width* nil)
@@ -557,7 +499,7 @@ the directories that appear in the value of that variable."
(format t "~%~a~%~%" (getf oneliner :oneliner))))
(defun cache-and-print-search-response (response)
- (cache-search-results-to-last-search-file
+ (merge-into-cache
(loop for oneliner in (getf (jonathan:parse response) :oneliners)
collect oneliner
do (print-oneliner-result-for-user oneliner))))
@@ -585,7 +527,6 @@ the directories that appear in the value of that variable."
(defun search-for-oneliners (terms limit not-flagged-p)
(assert (loop for term in terms never (find #\, term) ))
(set-term-width)
- (format t "TERM WIDTH IS ~a~%" *term-width*)
(ensure-config)
(let ((response
(api:request-with