├── README.md └── gorepl-mode.el /README.md: -------------------------------------------------------------------------------- 1 | [![MELPA](https://melpa.org/packages/gorepl-mode-badge.svg)](https://melpa.org/#/gorepl-mode) 2 | 3 | # gorepl-mode 4 | A minor emacs mode for Go REPL. 5 | 6 | 7 | ## Synopsis 8 | **gorepl-mode** is a Go REPL interaction library for Emacs. It's built on top of 9 | [gore](https://github.com/motemen/gore). 10 | 11 | 12 | Gorepl packs some features (in no particular order): 13 | 14 | * Powerful REPL - thanks to gore 15 | * Interactive code evaluation 16 | * Evaluate expressions selected 17 | * Launch a repl with loaded file in a context 18 | 19 | ## Installation 20 | 21 | `gorepl-mode` is available on [MELPA](http://melpa.org) 22 | 23 | You can install `gorepl-mode` with the following command: 24 | 25 | M-x package-install [RET] gorepl-mode [RET] 26 | 27 | 28 | ### Prerequisite 29 | 30 | Install [gore](https://github.com/motemen/gore). 31 | 32 | ### Basic configuration 33 | 34 | * Enable `gorepl-mode` in `go-mode` buffers: 35 | 36 | ```el 37 | (add-hook 'go-mode-hook #'gorepl-mode) 38 | ``` 39 | 40 | ## Keyboard shortcuts 41 | 42 | * M-x gorepl-run: Launch an instance of gore REPL client. 43 | 44 | * M-x gorepl-run-load-current-file: Launch an instance of gore REPL client with the current file loaded. 45 | 46 | ### gorepl-mode 47 | 48 | Keyboard shortcut | Description 49 | -------------------------------------|------------------------------- 50 | C-C C-g | Launch an instance of gore REPL client 51 | C-C C-l | Launch an instance of gore REPL client - with the current file loaded 52 | C-c C-e | Evaluate the region selected 53 | C-c C-r | Evaluate the current line 54 | 55 | ### Issues 56 | 57 | The performance for gore is not very fast, in fact when a gorepl is running using `gorepl-run-load-current-file` it can be slow at first, especially with large files, because it's doing `go run `to all file. 58 | 59 | 60 | ### TODO 61 | 62 | * Autocompletion 63 | * go-mode in gorepl-mode 64 | -------------------------------------------------------------------------------- /gorepl-mode.el: -------------------------------------------------------------------------------- 1 | ;;; gorepl-mode.el --- Go REPL Interactive Development in top of Gore -*- lexical-binding: t -*- 2 | 3 | ;; Copyright © 2015-2016 Manuel Alonso 4 | 5 | ;; Author: Manuel Alonso 6 | ;; Maintainer: Manuel Alonso 7 | ;; URL: http://www.github.com/manute/gorepl-mode 8 | ;; Version: 1.0.0 9 | ;; Package-Requires: ((emacs "24") (s "1.11.0") (f "0.19.0") (hydra "0.13.0")) 10 | ;; Keywords: languages, go, golang, gorepl 11 | 12 | ;; This file is NOT part of GNU Emacs. 13 | 14 | ;; This program is free software: you can redistribute it and/or modify 15 | ;; it under the terms of the GNU General Public License as published by 16 | ;; the Free Software Foundation, either version 3 of the License, or 17 | ;; (at your option) any later version. 18 | 19 | ;; This program is distributed in the hope that it will be useful, 20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | ;; GNU General Public License for more details. 23 | 24 | ;; You should have received a copy of the GNU General Public License 25 | ;; along with this program. If not, see . 26 | 27 | ;;; Commentary: 28 | ;; 29 | ;; This library provides a Go repl interactive development environment for Emacs, built on 30 | ;; top of Gore (https://github.com/motemen/gore). 31 | ;; 32 | ;;; Code: 33 | 34 | (require 's) 35 | (require 'f) 36 | (require 'hydra) 37 | 38 | (defgroup gorepl nil 39 | "GO repl interactive" 40 | :prefix "gorepl-" 41 | :group 'applications 42 | :link '(url-link :tag "Github" "https://github.com/manute/gorepl-mode") 43 | :link '(emacs-commentary-link :tag "Commentary" "gorepl")) 44 | 45 | (defcustom gorepl-command 46 | "gore" 47 | "The command used to execute gore." 48 | :type 'string 49 | :group 'gorepl) 50 | 51 | 52 | (defcustom gorepl-mode-hook nil 53 | "Hook called by `gorepl-mode'." 54 | :type 'hook 55 | :group 'gorepl) 56 | 57 | 58 | (defconst gorepl-version "1.0.0") 59 | (defconst gorepl-buffer "*Go REPL*") 60 | (defconst gorepl-buffer-name "Go REPL") 61 | 62 | 63 | ;; MANY THANKS to masteringenmacs for this: 64 | ;; https://www.masteringemacs.org/article/comint-writing-command-interpreter 65 | (defun gorepl--run-gore (args) 66 | "Run an inferior instance of `gore' inside Emacs." 67 | (let* ((buffer (comint-check-proc gorepl-buffer-name))) 68 | ;; pop to the "*GO REPL Buffer*" buffer if the process is dead, the 69 | ;; buffer is missing or it's got the wrong mode. 70 | (display-buffer 71 | (if (or buffer (not (derived-mode-p 'gorepl-mode)) 72 | (comint-check-proc (current-buffer))) 73 | (get-buffer-create (or buffer gorepl-buffer)) 74 | (current-buffer))) 75 | ;; create the comint process if there is no buffer. 76 | (unless buffer 77 | (apply 'make-comint-in-buffer gorepl-buffer-name buffer 78 | gorepl-command nil args) 79 | (gorepl-mode)))) 80 | 81 | 82 | ;;;;;;;;;;;;;;;;;;;;;;; 83 | ;; API 84 | ;;;;;;;;;;;;;;;;;;;;;; 85 | 86 | (defun gorepl-version () 87 | "Display GoREPL's version." 88 | (interactive) 89 | (message "GOREPL %s" gorepl-version)) 90 | 91 | (defun gorepl-run () 92 | "Start or switch to the GoREPL buffer" 93 | (interactive) 94 | (message "Entering gore session...") 95 | (gorepl--run-gore '())) 96 | 97 | (defun gorepl-eval (stmt) 98 | "Send `stmt' to gore, maybe starting it" 99 | (interactive) 100 | (gorepl-run) 101 | (with-current-buffer gorepl-buffer 102 | (insert stmt) 103 | (comint-send-input) 104 | (message (format "Just sent to gore: %s" stmt)))) 105 | 106 | (defun gorepl-eval-region (begin end) 107 | "Evaluate region selected." 108 | (interactive "r") 109 | (gorepl-mode t) 110 | (let ((cmd (buffer-substring begin end))) 111 | (gorepl-eval cmd))) 112 | 113 | (defun gorepl-eval-line (&optional arg) 114 | "Evaluate current line." 115 | (interactive "P") 116 | (unless arg 117 | (setq arg 1)) 118 | (when (> arg 0) 119 | (gorepl-eval-region 120 | (line-beginning-position) 121 | (line-end-position arg)))) 122 | 123 | (defun gorepl-run-load-current-file () 124 | "Run a GoREPL with a context file in it" 125 | (interactive) 126 | (gorepl--run-gore (list "-context" (buffer-file-name)))) 127 | 128 | (defun gorepl-import () 129 | "Import " 130 | (interactive) 131 | (catch 'err 132 | (let ((name (read-string "Package path? "))) 133 | (unless name 134 | (message "No package specified") 135 | (throw 'err nil)) 136 | (let ((name (s-trim (s-chomp name)))) 137 | (unless (s-present? name) 138 | (message "No package specified") 139 | (throw 'err nil)) 140 | (when (s-contains? " " name) 141 | (message "Package names can't contain a space") 142 | (throw 'err nil)) 143 | (message (format "Package specified: %s" name)) 144 | (let ((stmt (format ":import %s" name))) 145 | (gorepl-eval stmt)))))) 146 | 147 | (defun gorepl-print () 148 | "Print the source code from this session" 149 | (interactive) 150 | (gorepl-eval ":print")) 151 | 152 | (defun gorepl-write () 153 | "Write the source code from this session out to a file" 154 | (interactive) 155 | (let ((name (read-file-name "Output file name? "))) 156 | (message (format "Output file name: %s" name)) 157 | (let ((name (f-expand name))) 158 | (catch 'err 159 | (when (s-blank? name) 160 | (message "Aborted write: no file name given") 161 | (throw 'err nil)) 162 | (if (f-exists? name) (progn (message "Stomping: %s" name) (f-touch name)) 163 | (progn 164 | (f-write-text (format "// gore dump on `%s' by `%s'\n\n" 165 | (format-time-string 166 | "%a %b %d %H:%M:%S %Z %Y" 167 | (current-time)) 168 | (user-original-login-name)) 169 | 'utf-8 170 | name))) 171 | (let ((stmt (format ":write %s" name))) 172 | (gorepl-eval stmt)))))) 173 | 174 | (defun gorepl-doc () 175 | "Show documentation on 215 | _f_: Run this file | _k_: Line+Step | _y_: Print this source 216 | _q_: Quit Hydra | _K_: Line | _u_: Write this sourceto 217 | ^^| ^^| _o_: List `these' actual commands 218 | ^^| ^^| _r_: Restart this REPL 219 | ^^| ^^| _p_: Quit this REPL (or C-d) 220 | " 221 | ("d" gorepl-run) 222 | ("f" gorepl-run-load-current-file) 223 | ("j" gorepl-eval-region) 224 | ("r" gorepl-restart) 225 | ("k" gorepl-eval-line-goto-next-line :exit nil) 226 | ("K" gorepl-eval-line) 227 | ("t" gorepl-import) 228 | ("y" gorepl-print) 229 | ("u" gorepl-write) 230 | ("i" gorepl-doc) 231 | ("o" gorepl-help) 232 | ("p" gorepl-quit) 233 | ("q" nil)) 234 | 235 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 236 | ;; DEFINE MINOR MODE 237 | ;; 238 | ;; Many thanks -> https://github.com/ruediger/rusti.el 239 | ;; 240 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 241 | 242 | (defvar gorepl-mode-map 243 | (let ((map (make-sparse-keymap))) 244 | (define-key map (kbd "C-c C-g") #'gorepl-run) 245 | (define-key map (kbd "C-c C-l") #'gorepl-run-load-current-file) 246 | (define-key map (kbd "C-c C-e") #'gorepl-eval-region) 247 | (define-key map (kbd "C-c C-r") #'gorepl-eval-line) 248 | map) 249 | "Mode map for `gorepl-mode'.") 250 | 251 | (defcustom gorepl-mode-lighter " Gorepl" 252 | "Text displayed in the mode line (Lighter) if `gorepl-mode' is active." 253 | :group 'gorepl 254 | :type 'string) 255 | 256 | ;;;###autoload 257 | (define-minor-mode gorepl-mode 258 | "A minor mode for run a go repl on top of gore" 259 | :group 'gorepl 260 | :lighter gorepl-mode-lighter 261 | :keymap gorepl-mode-map) 262 | 263 | 264 | (provide 'gorepl-mode) 265 | ;;; gorepl-mode.el ends here 266 | --------------------------------------------------------------------------------