Skip to content

Instantly share code, notes, and snippets.

@cbilson
Created February 6, 2018 03:27
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save cbilson/ae0d90d163be4d769f8a15ddb58292bc to your computer and use it in GitHub Desktop.
Save cbilson/ae0d90d163be4d769f8a15ddb58292bc to your computer and use it in GitHub Desktop.
Rough draft: execute poweshell from emacs src blocks
;;; ob-powershell.el --- org-babel functions for powershell evaluation
;; Authors: Chris Bilson
;; Keywords: literate programming, reproducible research
;; Homepage: http://orgmode.org
;;; Commentary:
;; Org-Babel support for evaluating powershell source code.
;;; Code:
(require 'ob)
(require 'ob-core)
(eval-when-compile (require 'cl))
(defvar org-babel-tangle-lang-exts)
(add-to-list 'org-babel-tangle-lang-exts '("poweshell" . "ps1"))
(defvar org-babel-default-header-args:powershell '())
(defun org-babel-expand-body:powershell (body params)
body)
(defun org-babel-execute:powershell (body params)
"Execute a block of Powershell code with Babel.
This function is called by `org-babel-execute-src-block'."
(let* ((in-file (org-babel-temp-file "powershell-" ".ps1"))
(cmdline (or (cdr (assq :cmdline params))
"-NoLogo -NonInteractive"))
(cmd (or (cdr (assq :cmd params))
"powershell")))
(with-temp-file in-file
(insert (org-babel-expand-body:powershell body params)))
(message "cmd: %s" cmd)
(message "cmdline: %s" cmdline)
(message "in-file: %s" in-file)
(message "body: %s" (org-babel-expand-body:powershell body params))
(org-babel-eval
(concat cmd " " cmdline
" -File " (org-babel-process-file-name in-file))
"")))
;; TODO: I think I *can* support sessions in powershell and really want to...
(defun org-babel-prep-session:powershell (session params)
"Prepare SESSION according to the header arguments in PARAMS."
(error "Sessions are not supported for Powershell"))
(defun org-babel-variable-assignments:powershell (params)
"Return list of powershell statements assigning the block's variables."
(mapcar
(lambda (pair)
(org-babel-powershell--var-to-powershell (cdr pair) (car pair)))
(mapcar #'cdr (org-babel--get-vars params))))
;; helper functions
(defvar org-babel-powershell--lvl 0)
(defun org-babel-powershell--var-to-powershell (var &optional varn)
"Convert an elisp value to a powershell variable.
The elisp value, VAR, is converted to a string of powershell source code
specifying a var of the same value."
(if varn
(let ((org-babel-powershell--lvl 0) (lvar (listp var)) prefix)
(concat "$" (symbol-name varn) "=" (when lvar "\n")
(org-babel-powershell--var-to-powershell var)
";\n"))
(let ((prefix (make-string (* 2 org-babel-powershell--lvl) ?\ )))
(concat prefix
(if (listp var)
(let ((org-babel-powershell--lvl (1+ org-babel-powershell--lvl)))
(concat "[\n"
(mapconcat #'org-babel-powershell--var-to-powershell var "")
prefix "]"))
(format "${%s}" var))
(unless (zerop org-babel-powershell--lvl) ",\n")))))
(defvar org-babel-powershell-buffers '(:default . nil))
(defun org-babel-powershell-initiate-session (&optional session params)
"Return nil because sessions are not supported by powershell."
nil)
(defvar org-babel-powershell-preface nil)
(defun org-babel-powershell-evaluate (session ibody &optional result-type result-params)
"Pass BODY to the Powershell process in SESSION.
If RESULT-TYPE equals 'output then return a list of the outputs
of the statements in BODY, if RESULT-TYPE equals 'value then
return the value of the last statement in BODY, as elisp."
(when session (error "Sessions are not supported for Powershell"))
(let* ((body (concat org-babel-powershell-preface ibody))
(out-file (org-babel-temp-file "powershell-"))
(tmp-babel-file (org-babel-process-file-name
out-file 'noquote))
(in-file (org-babel-temp-file "powershell-"))
(command (format "%s -File '%s'" org-babel-powershell-command in-file)))
(with-temp-file in-file
(insert body))
(message "body: %s" body)
(message "in-file: %s" in-file)
(message "out-file: %s" out-file)
(message "command: %s" command)
(let ((results
(case result-type
(output
(with-temp-file out-file
(insert
(org-babel-eval org-babel-powershell-command body))
(buffer-string)))
(value
(message "evaliing now...")
(org-babel-eval command body)))))
(when results
(org-babel-result-cond result-params
(org-babel-eval-read-file out-file)
(org-babel-import-elisp-from-file out-file '(16)))))))
(provide 'ob-powershell)
;;; ob-powershell.el ends here
ls C:\Users

(org-babel-do-load-languages
 'org-babel-load-languages
 '((C . t)
   (clojure . t)
   (csharp . t)
   (ditaa . t)
   (dot . t)
   ;; (ipython . t)
   (http . t)
   (plantuml . t)
   (powershell . t)
   (python . t)
   (ruby . t)
   ;; (sh . t)
   ))
@priyadarshan
Copy link

priyadarshan commented Oct 22, 2019

I came here thanks to Babel with PowerShell on Reddit. I can confirm it works as intended (although perhaps a bit slow). Thank you for ob-powershell.el. You could consider adding it to Melpa, I am sure it would be useful to many,.

@cbilson
Copy link
Author

cbilson commented Jan 18, 2021

Not sure why I only now found your comments ... I actually came from the same reddit link :-)

I stopped using this a while ago because I got sick of emacs hanging on Windows and how much of a tax you pay using emacs on NTFS (things like magit are almost impossible to even have installed in emacs when you have large git repositories, for example.) I started using emacs from WSL and didn't bother trying to invoke powershell from it (you can sort of invoke Windows powershell from inside WSL, but I didn't really need to do this.)

Anyway, I switched back to using Windows for emacs again a few months ago: I found that if I build emacs myself and avoid some packages I would otherwise like to use (like magit) it ultimately doesn't hang and works better than through WSL most of the time, for me.

I just started using my version of the above gist a few weeks ago (I want to run dotnet script ... and it seemed like the easiest way to run it). I'll try with the changes you suggested, @fpvmorais. Thanks!

I also saw there is an ob-pwsh package on github, but wasn't really able to get that to work. This is kind of a yak-shave for me, so not committing lots of cycles to it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment