go-playground.el (7755B)
1 ;;; go-playground.el --- Local Golang playground for short snippets. 2 3 ;; Copyright (C) 2015-2024 Alexander I.Grafov and the project 4 ;; contibutors. 5 6 ;; Author: Alexander I.Grafov <grafov@inet.name> 7 ;; URL: https://github.com/grafov/go-playground 8 ;; Keywords: tools, golang 9 ;; Version: 1.8.2 10 ;; Package-Requires: ((emacs "24") (go-mode "1.4.0") (gotest "0.13.0")) 11 12 ;; This program is free software; you can redistribute it and/or modify 13 ;; it under the terms of the GNU General Public License as published by 14 ;; the Free Software Foundation, either version 3 of the License, or 15 ;; (at your option) any later version. 16 17 ;; This program is distributed in the hope that it will be useful, 18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 ;; GNU General Public License for more details. 21 22 ;; You should have received a copy of the GNU General Public License 23 ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 24 25 ;;; Commentary: 26 27 ;; Local playground for the Go programs similar to play.golang.org. 28 ;; `M-x go-playground` and type you golang code then make&run it with `C-Return`. 29 30 ;; Playground works around `go-mode` and requires preconfigured environment 31 ;; for Go language. 32 33 ;; I recommend you to use `goimports` instead of `gofmt` for automatically make 34 ;; import clauses. It very comfortable especially for experimenting with code 35 ;; in playground. 36 37 ;; You may push code to play.golang.org with go-mode' function `go-play-buffer`. 38 39 ;; 40 41 ;;; Code: 42 43 (require 'go-mode) 44 (require 'gotest) 45 (require 'compile) 46 (require 'time-stamp) 47 (require 'subr-x) 48 49 (defgroup go-playground nil 50 "Options specific to Go Playground." 51 :group 'go) 52 53 (defcustom go-playground-ask-file-name nil 54 "Non-nil means we ask for a name for the snippet. 55 56 By default it will be created as snippet.go" 57 :type 'boolean 58 :group 'go-playground) 59 60 (defcustom go-playground-confirm-deletion t 61 "Non-nil means you will be asked for confirmation on the snippet deletion with `go-playground-rm'. 62 63 By default confirmation required." 64 :type 'boolean 65 :group 'go-playground) 66 67 (defcustom go-playground-basedir "~/go/src/playground" 68 "Base directory for playground snippets. Better to set it under GOPATH." 69 :type 'file 70 :group 'go-playground) 71 72 (defcustom go-playground-compile-command (concat go-command " mod tidy; " go-command " run ./...") 73 "The commands used for compilation. 74 75 Use \";\" or any other appropriate shell separator if you need several commands in one session." 76 :type 'string 77 :group 'go-playground) 78 79 (defcustom go-playground-pre-rm-hook nil 80 "Hook run before a snippet is removed." 81 :type 'hook 82 :group 'go-playground) 83 84 (defcustom go-playground-init-command "go mod init" 85 "The shell command executed once when the snippet just created." 86 :type 'string 87 :group 'go-playground) 88 89 ;;;###autoload 90 (define-minor-mode go-playground-mode 91 "A place for playing with golang code and export it in short snippets." 92 :init-value nil 93 :lighter "Play(Go)" 94 :keymap '(([C-return] . go-playground-exec) 95 ([M-return] . go-playground-cmd))) 96 97 (defun go-playground-snippet-file-name(&optional snippet-name) 98 (let* ((file-name (cond (snippet-name) 99 (go-playground-ask-file-name 100 (read-string "Go Playground filename: ")) 101 ("snippet"))) 102 (snippet-dir (go-playground-snippet-unique-dir file-name))) 103 (let ((default-directory snippet-dir)) 104 (call-process-shell-command go-playground-init-command)) 105 (concat snippet-dir "/" file-name ".go"))) 106 107 ; 108 (defun go-playground-save-and-run () 109 "Obsoleted by go-playground-exec." 110 (interactive) 111 112 (go-playground-exec)) 113 114 (defun go-playground-exec () 115 "Save the buffer then runs Go compiler for executing the code." 116 (interactive) 117 (if (go-playground-inside) 118 (progn 119 (save-buffer t) 120 (make-local-variable 'compile-command) 121 (compile go-playground-compile-command)))) 122 123 (defun go-playground-cmd (cmd) 124 "Save the buffer then apply custom compile command from 125 minibuffer to the files or buffer." 126 (interactive "scompile command: ") 127 (if (go-playground-inside) 128 (progn 129 (save-buffer t) 130 (make-local-variable 'compile-command) 131 (compile cmd)))) 132 133 ;;;###autoload 134 (defun go-playground () 135 "Run playground for Go language in a new buffer." 136 (interactive) 137 (let ((snippet-file-name (go-playground-snippet-file-name))) 138 (switch-to-buffer (create-file-buffer snippet-file-name)) 139 (go-playground-insert-template-head "snippet of code") 140 (insert "package main 141 142 import ( 143 \"fmt\" 144 ) 145 146 func main() { 147 fmt.Println(\"Results:\") 148 } 149 ") 150 (backward-char 3) 151 (go-mode) 152 (go-playground-mode) 153 (set-visited-file-name snippet-file-name t))) 154 155 (defun go-playground-insert-template-head (description) 156 (insert "// -*- mode:go;mode:go-playground -*- 157 // " description " @ " (time-stamp-string "%:y-%02m-%02d %02H:%02M:%02S") " 158 159 // === Go Playground === 160 // Execute the snippet with: Ctl-Return 161 // Provide custom arguments to compile with: Alt-Return 162 // Other useful commands: 163 // - remove the snippet completely with its dir and all files: (go-playground-rm) 164 // - upload the current buffer to playground.golang.org: (go-playground-upload) 165 166 ")) 167 168 (defun go-playground-rm () 169 "Remove files of the current snippet together with directory of this snippet." 170 (interactive) 171 (if (go-playground-inside) 172 (if (or (not go-playground-confirm-deletion) 173 (y-or-n-p (format "Do you want delete whole snippet dir %s? " 174 (file-name-directory (buffer-file-name))))) 175 (progn 176 (save-buffer) 177 (run-hooks 'go-playground-pre-rm-hook) 178 (delete-directory (file-name-directory (buffer-file-name)) t t) 179 (kill-buffer))) 180 (message "Won't delete this! Because %s is not under the path %s. Remove the snippet manually!" 181 (buffer-file-name) go-playground-basedir))) 182 183 ;;;###autoload 184 (defun go-playground-remove-current-snippet () 185 "Obsoleted by `go-playground-rm'." 186 (interactive) 187 (go-playground-rm)) 188 189 ;;;###autoload 190 (defun go-playground-download (url) 191 "Download a paste from the play.golang.org and insert it in a new local playground buffer. 192 Tries to look for a URL at point." 193 (interactive (list (read-from-minibuffer "Playground URL: " (ffap-url-p (ffap-string-at-point 'url))))) 194 (with-current-buffer 195 (let ((url-request-method "GET") url-request-data url-request-extra-headers) 196 (url-retrieve-synchronously (concat url ".go"))) 197 (let* ((snippet-file-name (go-playground-snippet-file-name)) (buffer (create-file-buffer snippet-file-name))) 198 (goto-char (point-min)) 199 (re-search-forward "\n\n") 200 (copy-to-buffer buffer (point) (point-max)) 201 (kill-buffer) 202 (with-current-buffer buffer 203 (goto-char (point-min)) 204 (go-playground-insert-template-head (concat url " imported")) 205 (go-mode) 206 (go-playground-mode) 207 (set-visited-file-name snippet-file-name t) 208 (switch-to-buffer buffer))))) 209 210 (defun go-playground-upload () 211 "Upload the current buffer to play.golang.org and return the short URL of the playground." 212 (interactive) 213 (if (not (go-playground-inside)) 214 (message "Not in a Go Playground buffer!") 215 (go-play-buffer))) 216 217 (defun go-playground-snippet-unique-dir (prefix) 218 "Get unique directory under GOPATH/`go-playground-basedir`." 219 (let ((dir-name (concat go-playground-basedir "/" 220 (if (and prefix go-playground-ask-file-name) (concat prefix "-")) 221 (time-stamp-string "at-%:y-%02m-%02d-%02H%02M%02S")))) 222 (make-directory dir-name t) 223 dir-name)) 224 225 (defun go-playground-inside () 226 "Is the current buffer is valid go-playground buffer." 227 (and (bound-and-true-p go-playground-mode) 228 buffer-file-name 229 (string-prefix-p (file-truename go-playground-basedir) 230 (file-truename buffer-file-name)))) 231 232 (provide 'go-playground) 233 ;;; go-playground.el ends here