2424; ;; Commentary:
2525
2626; ; Local playground for the Rust programs similar to play.rust-lang.org.
27- ; ; `M-x rust-playground` and type you rust code then make&run it with `C-Return`.
27+ ; ; `M-x rust-playground` and type you rust code then make&run it with `C-c C-c`.
28+ ; ; Toggle between Cargo.toml and main.rs with `C-c b`
29+ ; ; Delete the current playground and close all buffers with `C-c k`
2830
2931; ; Playground works in conjunction with `rust-mode` and requires
3032; ; preconfigured environment for Rust language.
3133
3234; ; It is port of github.com/grafov/go-playground for Go language.
3335
34- ; ; You may push code to play.rust-lang.org with rust-mode' function `rust-playpen-buffer`.
35-
36- ; ;
37-
3836; ;; Code:
3937
40- (require 'rust-mode )
4138(require 'compile )
4239(require 'time-stamp )
4340
44- ; I think it should be defined in rust-mode.
45- (defcustom rust-playground-bin " rustc"
46- " The ’rust’ command."
47- :type 'string
48- :group 'rust-mode )
49-
5041(defgroup rust-playground nil
5142 " Options specific to Rust Playground."
5243 :group 'rust-mode )
5344
54- (defcustom rust-playground-ask-file-name nil
55- " Non-nil means we ask for a name for the snippet.
56-
57- By default it will be created as snippet.go"
58- :type 'boolean
45+ ; ; I think it should be defined in rust-mode.
46+ (defcustom rust-playground-run-command " cargo run"
47+ " The ’rust’ command."
48+ :type 'string
5949 :group 'rust-playground )
6050
6151(defcustom rust-playground-confirm-deletion t
@@ -70,120 +60,158 @@ By default confirmation required."
7060 :type 'file
7161 :group 'rust-playground )
7262
73- (defvar-local rust-playground-current-snippet-file " snippet.rs"
74- " The current snippet file." )
63+ (defcustom rust-playground-cargo-toml-template
64+ " [package]
65+ name = \" foo\"
66+ version = \" 0.1.0\"
67+ authors = [\" Rust Example <[email protected] >\" ] 68+
69+ [dependencies]"
70+ " When creating a new playground, this will be used as the Cargo.toml file" )
71+
72+ (defcustom rust-playground-main-rs-template
73+ " fn main() {
74+
75+ println!(\" Results:\" )
76+ }"
77+ " When creating a new playground, this will be used as the body of the main.rs file" )
7578
7679(define-minor-mode rust-playground-mode
77- " A place for playing with golang code and export it in short snippets."
80+ " A place for playing with Rust code and export it in short snippets."
7881 :init-value nil
7982 :lighter " Play(Rust)"
80- :keymap '(([C-return] . rust-playground-exec)))
81-
82- (defun rust-playground-snippet-file-name (&optional snippet-name )
83- (setq-local rust-playground-current-snippet-file
84- (let ((file-name
85- (cond (snippet-name)
86- (rust-playground-ask-file-name
87- (read-string " Rust Playground filename: " )) (" snippet" ))))
88- (concat (rust-playground-snippet-unique-dir file-name) " /" file-name " .rs" ))))
83+ :keymap (let ((map (make-sparse-keymap )))
84+ (define-key map (kbd " C-c C-c" ) 'rust-playground-exec )
85+ (define-key map (kbd " C-c b" ) 'rust-playground-switch-between-cargo-and-main )
86+ (define-key map (kbd " C-c k" ) 'rust-playground-rm )
87+ map))
88+
89+ (defun rust-playground-dir-name (&optional snippet-name )
90+ " Get the name of the directory where the snippet will exist, with SNIPPET-NAME as part of the directory name."
91+ (file-name-as-directory (concat (file-name-as-directory rust-playground-basedir)
92+ (time-stamp-string " at-%:y-%02m-%02d-%02H%02M%02S" ))))
93+
94+ (defun rust-playground-snippet-main-file-name (basedir )
95+ " Get the snippet main.rs file from BASEDIR."
96+ (concat basedir (file-name-as-directory " src" ) " main.rs" ))
97+
98+ (defun rust-playground-toml-file-name (basedir )
99+ " Get the cargo.toml filename from BASEDIR."
100+ (concat basedir " Cargo.toml" ))
101+
102+ (defun rust-playground-get-snippet-basedir (&optional path )
103+ " Get the path of the dir containing this snippet.
104+ Start from PATH or the path of the current buffer's file, or NIL if this is not a snippet."
105+ (unless path
106+ (setq path (buffer-file-name )))
107+ (if (not path)
108+ nil
109+ (if (not (string= path " /" ))
110+ (let ((base " /home/jason/.emacs.d/rust-playground" )
111+ (path-parent (file-name-directory (directory-file-name path))))
112+ (if (string= (file-name-as-directory base)
113+ (file-name-as-directory path-parent))
114+ path
115+ (rust-playground-get-snippet-basedir path-parent)))
116+ nil )))
117+
118+ ; ; I think the proper way to check for this is to test if the minor mode is active
119+ ; ; TODO base this check off the minor mode, once that mode gets set on all files
120+ ; ; in a playground
121+ (defmacro in-rust-playground (&rest forms )
122+ " Execute FORMS if current buffer is part of a rust playground.
123+ Otherwise message the user that they aren't in one."
124+ `(if (not (rust-playground-get-snippet-basedir))
125+ (message " You aren't in a Rust playground. " )
126+ ,@forms ))
89127
90128(defun rust-playground-exec ()
91129 " Save the buffer then run Rust compiler for executing the code."
92130 (interactive )
93- (make-local-variable 'compile-command )
94- (let ((snippet-file buffer-file-name))
95- (save-buffer t )
96- (compile " cargo build" )))
131+ (in-rust-playground
132+ (save-buffer t )
133+ (compile rust-playground-run-command)))
97134
98135;;;### autoload
99136(defun rust-playground ()
100137 " Run playground for Rust language in a new buffer."
101138 (interactive )
102- (let ((snippet-file-name (rust-playground-snippet-file-name)))
103- (switch-to-buffer (create-file-buffer snippet-file-name))
104- (add-hook 'kill-buffer-hook 'rust-playground-on-buffer-kill nil t )
105- (rust-playground-insert-template-head " snippet of code" )
106- (insert " fn main() {
107-
108- println!(\" Results:\" )
109-
110- }" )
111- (backward-char 3 )
112- (rust-mode )
139+ ; ; get the dir name
140+ (let* ((snippet-dir (rust-playground-dir-name))
141+ (snippet-file-name (rust-playground-snippet-main-file-name snippet-dir))
142+ (snippet-cargo-toml (rust-playground-toml-file-name snippet-dir)))
143+ ; ; create a buffer for Cargo.toml and switch to it
144+ (make-directory snippet-dir)
145+ (set-buffer (create-file-buffer snippet-cargo-toml))
146+ (set-visited-file-name snippet-cargo-toml t )
113147 (rust-playground-mode)
114- (set-visited-file-name snippet-file-name t )))
115-
116- ; remove compiled binary from snippet dir but not touch source files ;
117- (defun rust-playground-on-buffer-kill ()
118- (if (string-match-p (file-truename rust-playground-basedir) (file-truename (buffer-file-name )))
119- (delete-file (concat (file-name-directory (buffer-file-name )) " snippet" ))))
120-
121- (defun rust-playground-insert-template-head (description )
122- (insert " // -*- mode:rust;mode:rust-playground -*-
123- // " description " @ " (time-stamp-string " %:y-%02m-%02d %02H:%02M:%02S" ) "
124-
125- // === Rust Playground ===
126- // Execute the snippet with Ctl-Return
127- // Remove the snippet completely with its dir and all files M-x `rust-playground-rm`
128-
129- " ))
148+ (rust-playground-insert-template-head " snippet of code" snippet-dir)
149+ (insert rust-playground-cargo-toml-template)
150+ (save-buffer )
151+ ; ;now do src/main.rs
152+ (make-directory (concat snippet-dir " src" ))
153+ (switch-to-buffer (create-file-buffer snippet-file-name))
154+ (set-visited-file-name snippet-file-name t )
155+ (rust-playground-insert-template-head " snippet of code" snippet-dir)
156+ (insert rust-playground-main-rs-template)
157+ ; ; back up to a good place to edit from
158+ (backward-char 27 )
159+ (rust-playground-mode)))
160+
161+ (defun rust-playground-switch-between-cargo-and-main ()
162+ " Change buffers between the main.rs and Cargo.toml files for the current snippet."
163+ (interactive )
164+ (in-rust-playground
165+ (let ((basedir (rust-playground-get-snippet-basedir)))
166+ ; ; If you're in a rust snippet, but in some file other than main.rs,
167+ ; ; then just switch to main.rs
168+ (cond
169+ ; ; how to switch to existing or create new, given filename?
170+ ((string= " main.rs" (file-name-nondirectory buffer-file-name))
171+ (find-file (rust-playground-toml-file-name basedir)))
172+ (t
173+ (find-file (rust-playground-snippet-main-file-name basedir)))))))
174+
175+ (defun rust-playground-insert-template-head (description basedir )
176+ " Inserts a template about the snippet into the file."
177+ (let ((starting-point (point )))
178+ (insert (format
179+ " %s @ %s
180+
181+ === Rust Playground ===
182+ This snippet is in: %s
183+
184+ Execute the snippet: C-c C-c
185+ Delete the snippet completely: C-c k
186+ Toggle between main.rs and Cargo.toml: C-c b
187+
188+ " description (time-stamp-string " %:y-%02m-%02d %02H:%02M:%02S" ) basedir))
189+ (comment-region starting-point (point ))))
190+
191+ (defun rust-playground-get-all-buffers ()
192+ " Get all the buffers visiting Cargo.toml or any *.rs file under src/."
193+ (in-rust-playground
194+ (let* ((basedir (rust-playground-get-snippet-basedir))
195+ (srcdir (concat basedir (file-name-as-directory " src" ))))
196+ ; ; now get the fullpath of cargo.toml, and the fullpath of every file under src/
197+ (remove 'nil (seq-map 'find-buffer-visiting
198+ (cons (concat basedir " Cargo.toml" )
199+ (directory-files srcdir t " .*\. rs" )))))))
130200
131201;;;### autoload
132202(defun rust-playground-rm ()
133203 " Remove files of the current snippet together with directory of this snippet."
134204 (interactive )
135- (if (rust-playground-inside)
136- (if (or (not rust-playground-confirm-deletion)
137- (y-or-n-p (format " Do you want delete whole snippet dir %s ? "
138- (file-name-directory (buffer-file-name )))))
139- (progn
140- (save-buffer )
141- (delete-directory (file-name-directory (buffer-file-name )) t t )
142- (remove-hook 'kill-buffer-hook 'rust-playground-on-buffer-kill t )
143- (kill-buffer )))
144- (message " Won't delete this! Because %s is not under the path %s . Remove the snippet manually! "
145- (buffer-file-name ) rust-playground-basedir)))
146-
147- ; ; ;;;###autoload
148- ; ; (defun rust-playground-download (url)
149- ; ; "Download a paste from the play.golang.org and insert it in a new local playground buffer.
150- ; ; Tries to look for a URL at point."
151- ; ; (interactive (list (read-from-minibuffer "Playground URL: " (ffap-url-p (ffap-string-at-point 'url)))))
152- ; ; (with-current-buffer
153- ; ; (let ((url-request-method "GET") url-request-data url-request-extra-headers)
154- ; ; (url-retrieve-synchronously (concat url ".go")))
155- ; ; (let* ((snippet-file-name (rust-playground-snippet-file-name)) (buffer (create-file-buffer snippet-file-name)))
156- ; ; (goto-char (point-min))
157- ; ; (re-search-forward "\n\n")
158- ; ; (copy-to-buffer buffer (point) (point-max))
159- ; ; (kill-buffer)
160- ; ; (with-current-buffer buffer
161- ; ; (goto-char (point-min))
162- ; ; (rust-playground-insert-template-head (concat url " imported"))
163- ; ; (rust-mode)
164- ; ; (rust-playground-mode)
165- ; ; (set-visited-file-name snippet-file-name t)
166- ; ; (switch-to-buffer buffer)))))
167-
168- ; ; (defun rust-playground-upload ()
169- ; ; "Upload the current buffer to play.rust-lang.org and return the short URL of the playground."
170- ; ; (interactive)
171- ; ; (goto-char (point-min))
172- ; ; (forward-line)
173- ; ; (insert (rust-playpen-buffer)))
174-
175- (defun rust-playground-snippet-unique-dir (prefix )
176- " Get unique directory under `rust-playground-basedir`."
177- (let ((dir-name (concat rust-playground-basedir " /"
178- (if (and prefix rust-playground-ask-file-name) (concat prefix " -" ))
179- (time-stamp-string " at-%:y-%02m-%02d-%02H%02M%02S" ))))
180- (make-directory dir-name t )
181- dir-name))
182-
183- (defun rust-playground-inside ()
184- " It checks that minor mode is rusl-playground and buffer file placed under default directory."
185- (if (string-match-p (file-truename rust-playground-basedir) (file-truename (buffer-file-name )))
186- (bound-and-true-p rust-playground-mode)))
205+ (in-rust-playground
206+ (let ((playground-basedir (rust-playground-get-snippet-basedir)))
207+ (if playground-basedir
208+ (when (or (not rust-playground-confirm-deletion)
209+ (y-or-n-p (format " Do you want delete whole snippet dir %s ? "
210+ playground-basedir)))
211+ (dolist (buf (rust-playground-get-all-buffers))
212+ (kill-buffer buf))
213+ (delete-directory playground-basedir t t ))
214+ (message " Won't delete this! Because %s is not under the path %s . Remove the snippet manually! " (buffer-file-name ) rust-playground-basedir)))))
187215
188216(provide 'rust-playground )
189217; ;; rust-playground.el ends here
0 commit comments