After asking some questions and reading docs and getting some help, I have my own example of how to build a Transient. I honestly like this a lot better for this kind of work than Hydra; Hydra is fantastic, but this saves me a lot of duplicate work that I was doing with defining keymaps to assemble the right combination of arguments to pass to build functions. My usage of lisp is what I'd consider fairly simple. I'm just trying to build a command line to feed to `compile`.
;; -*- lexical-binding: t; -*-
;; Functions for building unreal on PROJ using dir-locals
(defconst +proj-target-list+ '("Development" "Debug" "Release"))
(defconst +proj-executable-list+ '("UnrealHeadertool" "ProjEditor" "ProjGame"))
(defconst +proj-platform-list+ '("Win64"))
(defconst +uproject-path+ "Proj/Proj.uproject")
(defconst +batch-builder+ "Engine/Build/BatchFiles/Build.bat")
(defconst +genproj-bat+ "GenerateProjectFiles.bat")
(defun nop ()
(interactive)
)
;; For future queue work, args can be stuffed into a list and executed later.
(transient-define-suffix proj-transient-build-unreal (args)
"Build Unreal"
:description "Build using current overrides"
(interactive (list (transient-args transient-current-command)))
(when-let ((build-path (getvar base-path)))
(let* ((exe (transient-arg-value "--exe=" args))
(target (transient-arg-value "--target=" args))
(platform (transient-arg-value "--platform=" args))
(debug (s-present? (transient-arg-value "--debug" args)))
(command (concat build-path +batch-builder+ " " exe " " platform " " target)))
(setq args (cl-remove "--exe=" args :test #'s-contains?))
(setq args (cl-remove "--target=" args :test #'s-contains?))
(setq args (cl-remove "--platform=" args :test #'s-contains?))
(when (s-contains? "Editor" exe)
(setq command (concat command " \"" build-path +uproject-path+ "\"")))
(setq command (concat command " " (mapconcat #'identity args " ")))
(if debug
(message command)
(compile command)))))
(transient-define-suffix proj-transient-genproj (args)
"Generate projects"
:description "Generate the project files"
(interactive (list (transient-args transient-current-command)))
(setq args (cl-remove "--exe=" args :test #'s-contains?))
(setq args (cl-remove "--target=" args :test #'s-contains?))
(setq args (cl-remove "--platform=" args :test #'s-contains?))
(when-let ((build-path (getvar base-path)))
(if (s-present? (transient-arg-value "--debug" args))
(message (concat build-path +genproj-bat+ " " (mapconcat #'identity args " ")))
(compile (concat build-path +genproj-bat+ " " (mapconcat #'identity args " "))))))
;; Define a transient to set all the arguments.
(transient-define-prefix proj-transient-build ()
"A transient to construct build strings and execute them."
["Build Configuration"
["Current Project Environment"
("P" proj-set-project-override)
("//" nop :description (lambda () (format "Base Dir: \"%s\"" (getvar base-path))) :transient t)
("--" nop :description (lambda () (format "UProject Path: \"%s\"" +uproject-path+)) :transient t)
("@@" nop :description (lambda () (format "Batch Builder: \"%s\"" +batch-builder+)) :transient t)
("++" nop :description (lambda () (format "Genproj bat: \"%s\"" +genproj-bat+)) :transient t)
("%%" nop :description (lambda () (format "Search Instance: \"%s\"" (getvar helm-locate-instance))) :transient t)]
["Build args"
("-p" "Show Progress" "-progress")
("-N" "No Hot Reload" "-NoHotreloadfromide")
("-D" "Preview Build" "--debug")]
["Targets"
("e" "Set Executable" "--exe=" :always-read t :choices ,+proj-executable-list+)
("t" "Set Target" "--target=" :always-read t :choices ,+proj-target-list+)
("p" "Set Platform" "--platform=" :always-read t :choices ,+proj-platform-list+)]]
["Execute Commands"
("g" "Generate Projects" proj-transient-genproj)
("B" "Build Project" proj-transient-build-unreal)])
Some notes:
* `getvar` is a macro I've written that returns the value of a variable that may or may not be set in a dir-locals.el file, and may furthermore be overridden. I need it because I often have to work in multiple different projects, but I'm still only compiling ONE SPECIFIC project, no matter what base directory I happen to be in.
* `nop` is there so I can have entries that are purely informative. If you type out the keys corresponding to the option, it just exits and does nothing. It's a hack, since Transient doesn't like entries that are only descriptions. Still, it works just fine.
* Unless you really need to for some other reason, try to work within these transient arguments. That is, unless you're trying to set a lisp variable for some other specific purpose, don't do what I did and write infixes that set lisp variables that you later access to do the compilation. It works just fine, but it's honestly more cumbersome and takes up more space. If your use case is like mine here--actually building a command to execute--the full-on Transient way ends up being cleaner.