3

I'd like to run an interactive CLI program from within Clojure (e.g., vim) and be able to interact with it.

In bash and other programming languages, I can do that with

vim > `tty`

I tried to do the same in Clojure:

(require '[clojure.java.shell :as shell])
(shell/sh "vim > `tty`")

but it just opens vim without giving me tty.


Background: I'm developing a Clojure CLI tool which parses emails and lets a user edit the parsed data before saving them on the disk. It works the following way:

  1. Read a file with email content and parse it. Each email is stored as a separate file.
  2. Show a user the parsed data and let the user edit the data in vim. Internally I create a temporary file with the parsed data, but I don't mind doing it another way if that would solve my issue.
  3. After a user finished editing the parsed data (they might decide to keep it as it is) append the data to a file on a disk. So all parsed data are saved to the same file.
  4. Go to 1st step if there are any files with emails left.
4
  • 1
    For vim/clojure interaction, look up fireplace Commented Jun 11, 2019 at 15:55
  • @AlanThompson thanks for your suggestion! Unfortunately, it isn't what I'm looking for. I added more background information to my question. Commented Jun 12, 2019 at 9:49
  • Can you add more background? If I understand correctly your program generates some files; let users edit them; then process the edited data(?). If so, you could dump your data in some temporary file, then fire e.g. (or (System/getenv "EDITOR") "vim") on the file; then read the file in your program when the user has saved the file and closed their editor. This is how git commit works. Commented Jun 18, 2019 at 11:45
  • Sure. I added more information about the program. I hope that helps! The issue is that I can't fire vim or any other editor from within Clojure program while also letting a user interact with this editor. Commented Jun 18, 2019 at 15:27

1 Answer 1

3

This code relies on Clojure Java interop to make use of Java's ProcessBuilder class.

(defn -main
[]
;use doseq instead of for because for is lazily evaluated
(doseq [i [1 2 3]]
;extract current directory from system variable
(let [file-name (str "test" i ".txt")
      working-directory (trim-newline (:out (sh "printenv" "PWD")))]
  (spit file-name "")

  ;this is where fun begins. We use ProcessBuilder to forward commands to terminal
  ;we pass a list of commands and their arguments to its constructor
  (let [process-builder (java.lang.ProcessBuilder. (list "vim" (str working-directory "/" file-name)))
  ;inherit is a configuration constant
        inherit (java.lang.ProcessBuilder$Redirect/INHERIT)]
  ;we configure input, output and error redirection
  (.redirectOutput process-builder inherit)
  (.redirectError process-builder inherit)
  (.redirectInput process-builder inherit)

  ;waitFor used to block execution until vim is closed
  (.waitFor (.start process-builder))
  )
  ;additional processing here
)
)
;not necessary but script tends to hang for around 30 seconds at end of its execution
;so this command is used to terminate it instantly
(System/exit 0)
)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.