Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.11 #11

Merged
merged 19 commits into from
Sep 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 125 additions & 54 deletions rustic-babel.el
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

;;; Commentary:

;; Async org-babel execution using cargo. Building and running is seperated
;; Async org-babel execution using cargo. Building and running is seperated
;; into two processes, as it's easier to get the output for the result of the
;; current source block.

Expand All @@ -15,6 +15,7 @@
(require 'ob-core)

(require 'rustic-cargo)
(require 'rustic-compile)

(add-to-list 'org-babel-tangle-lang-exts '("rustic" . "rs"))

Expand All @@ -38,7 +39,7 @@
(defvar rustic-babel-process-name "rustic-babel-process"
"Process name for org-babel rust compilation processes.")

(defvar rustic-babel-compilation-buffer "*rustic-babel-compilation-buffer*"
(defvar rustic-babel-compilation-buffer-name "*rustic-babel-compilation-buffer*"
"Buffer name for org-babel rust compilation process buffers.")

(defvar rustic-babel-dir nil
Expand All @@ -50,16 +51,19 @@
(defvar rustic-babel-params nil
"Babel parameters.")

(defvar rustic-babel-spinner nil)

(defun rustic-babel-eval (dir)
"Start a rust babel compilation process in directory DIR."
(let* ((err-buff (get-buffer-create rustic-babel-compilation-buffer))
(let* ((err-buff (get-buffer-create rustic-babel-compilation-buffer-name))
(default-directory dir)
(coding-system-for-read 'binary)
(process-environment (nconc
(list
(format "TERM=%s" "ansi")
(format "RUST_BACKTRACE=%s" rustic-compile-backtrace))
process-environment))
(process-environment
(nconc
(list
(format "TERM=%s" "ansi")
(format "RUST_BACKTRACE=%s" rustic-compile-backtrace))
process-environment))
(params '("cargo" "build"))
(inhibit-read-only t))
(with-current-buffer err-buff
Expand All @@ -73,45 +77,102 @@
:buffer err-buff
:command params
:filter #'rustic-compilation-filter
:sentinel #'rustic-babel-sentinel)))

(defun rustic-babel-sentinel (proc string)
"Sentinel for rust babel compilation processes.
:sentinel #'rustic-babel-build-sentinel)))

Use cargo run to get the results for org-babel.
If `rustic-babel-format-src-block' is t, format src-block after successful
(defun rustic-babel-build-sentinel (proc _output)
"Sentinel for rust babel compilation process PROC.
If `rustic-babel-format-src-block' is t, format src-block after successful
execution with rustfmt."
(let ((proc-buffer (process-buffer proc))
(inhibit-read-only t))
(if (zerop (process-exit-status proc))
(let* ((default-directory rustic-babel-dir)
(result (shell-command-to-string "cargo run --quiet"))
(result-params (list (cdr (assq :results rustic-babel-params))))
(params rustic-babel-params)
(marker rustic-babel-src-location))
(let* ((default-directory rustic-babel-dir))
(unless rustic-babel-display-compilation-buffer
(kill-buffer proc-buffer))
(with-current-buffer (marker-buffer marker)
(goto-char marker)
(org-babel-remove-result rustic-info)
(org-babel-insert-result result result-params rustic-info)
(if rustic-babel-format-src-block
(let ((babel-body (org-element-property :value (org-element-at-point)))
(proc (make-process :name "rustic-babel-format"
:buffer "rustic-babel-format-buffer"
:command `(,rustic-rustfmt-bin)
:filter #'rustic-compilation-filter
:sentinel #'rustic-babel-format-sentinel)))
(while (not (process-live-p proc))
(sleep-for 0.01))
(process-send-string proc babel-body)
(process-send-eof proc)))))
(pop-to-buffer proc-buffer)))
(when rustic-babel-display-spinner
(rustic-stop-spinner)
(setq mode-line-process nil)))

;; format babel block
(when rustic-babel-format-src-block
(let ((babel-body
(org-element-property :value (org-element-at-point)))
(proc
(make-process :name "rustic-babel-format"
:buffer "rustic-babel-format-buffer"
:command `(,rustic-rustfmt-bin)
:filter #'rustic-compilation-filter
:sentinel #'rustic-babel-format-sentinel)))
(while (not (process-live-p proc))
(sleep-for 0.01))
(process-send-string proc babel-body)
(process-send-eof proc)
(while (eq (process-status proc) 'run)
(sit-for 0.1))))

;; run project
(let* ((err-buff (get-buffer-create rustic-babel-compilation-buffer-name))
(dir default-directory)
(coding-system-for-read 'binary)
(process-environment
(nconc
(list
(format "TERM=%s" "ansi")
(format "RUST_BACKTRACE=%s" rustic-compile-backtrace))
process-environment))
(params '("cargo" "run" "--quiet"))
(inhibit-read-only t))
(with-current-buffer err-buff
(erase-buffer)
(setq-local default-directory dir)
(rustic-compilation-mode))
(when rustic-babel-display-compilation-buffer
(display-buffer err-buff))
(make-process
:name rustic-babel-process-name
:buffer err-buff
:command params
:filter #'rustic-compilation-filter
:sentinel #'rustic-babel-run-sentinel)))
(let (result)
(with-current-buffer proc-buffer
(setq result
(nth 3 (reverse
(split-string
(buffer-substring-no-properties (point-min) (point-max)) "\n" )))))
(rustic-babel-update-result-block result))
(with-rustic-spinner rustic-babel-spinner nil nil)
(pop-to-buffer proc-buffer))))

(defun rustic-babel-run-sentinel (proc _output)
"Sentinel for babel project execution."
(let ((proc-buffer (process-buffer proc))
result)
(if (zerop (process-exit-status proc))
(progn
(with-current-buffer proc-buffer
(setq result (buffer-string)))
(rustic-babel-update-result-block result)
(with-rustic-spinner rustic-babel-spinner nil nil)
(kill-buffer proc-buffer))
(progn
(with-current-buffer proc-buffer
(setq result
(car (split-string
(buffer-substring-no-properties (point-min) (point-max)) "\n" ))))
(rustic-babel-update-result-block result)
(with-rustic-spinner rustic-babel-spinner nil nil)
(pop-to-buffer proc-buffer)))))

(defun rustic-babel-update-result-block (result)
"Update result block with RESULT."
(let ((marker rustic-babel-src-location)
(result-params (list (cdr (assq :results rustic-babel-params)))))
(with-current-buffer (marker-buffer marker)
(goto-char marker)
(org-babel-remove-result rustic-info)
(org-babel-insert-result result result-params rustic-info))))

(defun rustic-babel-format-sentinel (proc output)
"This sentinel is used by the process `rustic-babel-format', that runs
after successful compilation."
(let ((proc-buffer (process-buffer proc))
(marker rustic-babel-src-location))
(save-excursion
Expand All @@ -125,15 +186,21 @@ execution with rustfmt."
(kill-buffer "rustic-babel-format-buffer")))

(defun rustic-babel-generate-project (&optional expand)
"Create rust project in `org-babel-temporary-directory'."
"Create rust project in `org-babel-temporary-directory'.
Return full path if EXPAND is t."
(let* ((default-directory org-babel-temporary-directory)
(dir (make-temp-file-internal "cargo" 0 "" nil)))
(shell-command-to-string (format "cargo new %s --bin --quiet" dir))
(if expand
(if expand
(concat (expand-file-name dir) "/")
dir)))

(defun rustic-babel-project ()
"In order to reduce the execution time when the project has
dependencies, the project name is stored as a text property in the
header of the org-babel block to check if the project already exists
in `org-babel-temporary-directory'. If the project exists, reuse it.
Otherwise create it with `rustic-babel-generate-project'."
(let* ((beg (org-babel-where-is-src-block-head))
(end (save-excursion (goto-char beg)
(line-end-position)))
Expand All @@ -142,14 +209,16 @@ execution with rustfmt."
(path (concat org-babel-temporary-directory "/" project "/")))
(if (file-directory-p path)
(progn
(put-text-property beg end 'project (make-symbol project))
(put-text-property beg end 'project (make-symbol project))
project)
(let ((new (rustic-babel-generate-project)))
(put-text-property beg end 'project (make-symbol new))
new)))))

(defun rustic-babel-cargo-toml (dir params)
"Append crates to Cargo.toml."
"Append crates to Cargo.toml.
Use org-babel parameter crates from PARAMS and add them to the project in
directory DIR."
(let ((crates (cdr (assq :crates params)))
(toml (expand-file-name "Cargo.toml" dir))
(str ""))
Expand All @@ -164,26 +233,28 @@ execution with rustfmt."
(insert str)))))

(defun org-babel-execute:rustic (body params)
"Execute a block of Rust code with Babel."
(when-let (p (process-live-p (get-process rustic-babel-process-name)))
(rustic-kill-live-process-p p))
"Execute a block of Rust code with org-babel."
(when-let* ((p (process-live-p (get-process rustic-babel-process-name))))
(rustic-process-kill-p p))
(let* ((default-directory org-babel-temporary-directory)
(project (rustic-babel-project))
(dir (setq rustic-babel-dir (expand-file-name project)))
(main (expand-file-name "main.rs" (concat dir "/src"))))
(main (expand-file-name "main.rs" (concat dir "/src"))))
(rustic-babel-cargo-toml dir params)
(setq rustic-info (org-babel-get-src-block-info))
(setq rustic-babel-params params)
(when rustic-babel-display-spinner
(setq mode-line-process
'(rustic-spinner
(":Executing " (:eval (spinner-print rustic-spinner)))))
(rustic-start-spinner))

(with-rustic-spinner rustic-babel-spinner
(make-spinner rustic-spinner-type t 10)
'(rustic-babel-spinner (":Executing " (:eval (spinner-print rustic-babel-spinner))))
(spinner-start rustic-babel-spinner))

(let ((default-directory dir))
(write-region (concat "#![allow(non_snake_case)]\n" body) nil main nil 0)
(write-region
(concat "#![allow(non_snake_case)]\n" body) nil main nil 0)
(rustic-babel-eval dir)
(setq rustic-babel-src-location (set-marker
(make-marker) (point) (current-buffer)))
(setq rustic-babel-src-location
(set-marker (make-marker) (point) (current-buffer)))
project)))

(provide 'rustic-babel)
Expand Down
Loading