Untitled

Running Programs in Next

Next gives you the power to extend its capabilities by invoking other programs. This allows you to chain Lisp functions and operations with any programs of your choosing.

A Practical Example: Download Youtube Videos

Let's consider the following situation: you are watching a Youtube video, but wait! You have somewhere to be! Why not download the video so you can watch it on the train where your internet access will be limited? Sounds good right? Let's see what options there are.

A quick search on the internet reveals a very convenient program called youtube-dl. Youtube-dl is invoked via the command line like this: youtube-dl "url-to-download". Of course there are other flags and arguments, but let's consider the simplest case.

You could simply copy the URL and paste it into the terminal. That's not really cool though. Let's automate it:

(define-command youtube-dl-current-page ()
  "Download a Youtube video in the currently open buffer."
  (with-result (url (buffer-get-url))
    (uiop:run-program
     (list "youtube-dl" url))))

Just like that! Instant Youtube downloader script. If you want, bind it to a keybinding:

(define-key *global-map* (key "C-c d") 'youtube-dl-current-page)

Now whenever you want to download a Youtube video simply "C-c d" and you'll have it on your computer!

"Shell" Mode

That was cool, but it doesn't really show the true power of Next. An example that is more illustrative is a very basic "shell" implementation. It is not really a shell because it doesn't have variables, piping, or anything else a shell really has, but it can run programs.

Simply put: shell mode is an extension for running simple one-liner shell commands and seeing the output. There is no history or variables. Use C-x s to enter *shell-mode* and c to enter a command and k to clear the command output.

(defvar *shell-mode-map* (make-hash-table :test 'equalp))

(define-parenstatic clear-shell-output
    (setf (ps:chain document body inner-h-t-m-l) ""))

(define-parenscript append-output (output)
  (setf (ps:chain document body inner-h-t-m-l)
        (ps:chain document body inner-h-t-m-l
                  (concat (ps:lisp
                           (concatenate 'string output "<br />"))))))

(define-command run-shell-command ()
  "Run a shell command and append the output into the buffer."
  (with-result (command (read-from-minibuffer
                         *minibuffer*
                         :input-prompt "Command:"))
    (buffer-evaluate-javascript
     *interface*
     (active-buffer *interface*)
     (append-output
      (uiop:run-program command :force-shell t :output :string)))))

(defun open-new-shell ()
  (let ((buffer (make-buffer "*shell*" (shell-mode))))
    (set-active-buffer *interface* buffer)))


(define-mode shell-mode (mode) ()
  "Shell mode is used as a pseudo shell.")

(define-key *shell-mode-map* (key "C-c") 'run-shell-command)
(define-key *shell-mode-map* (key "C-k") 'clear-shell-output)

(defun shell-mode ()
  (make-instance 'shell-mode
         :name "shell-mode"
         :keymap *shell-mode-map*))

(define-key *global-map* (key "C-x s") #'open-new-shell)

Within 40 lines of Lisp we've managed to make a completely new mode that allows us to execute commands, view the output on screen, and even clear the screen.

The Take Away

Next can be extended with any program of any type as long as it accepts some form of IPC. This means that you can chain any process you want on your computer. Of course, this application has bounds of reason, you shouldn't imagine that Next will turn into your system shell, process manager- even though it could.

Thanks for reading!