inferior-picolisp.el (15433B)
1 ;;;;;; inferior-picolisp: Picolisp repl in a buffer. 2 ;;;;;; Version: 1.2 3 4 ;;; Copyright (c) 2009, 2012, 2013, Guillermo R. Palavecino, Thorsten Jolitz 5 6 ;; This file is NOT part of GNU emacs. 7 8 ;;;; Credits: 9 ;; It's and adaptation of GNU emacs' cmuscheme.el 10 ;; 11 ;;;; Contact: 12 ;; For comments, bug reports, questions, etc, you can contact me via IRC 13 ;; to the user named grpala (or armadillo) on irc.freenode.net in the 14 ;; #picolisp channel or via email to the author's nickname at gmail.com 15 ;; 16 ;;;; License: 17 ;; This work is released under the GPL 2 or (at your option) any later 18 ;; version. 19 20 (require 'picolisp) 21 (require 'comint) 22 23 24 (defgroup picolisp nil 25 "Run an Picolisp process in a buffer." 26 :group 'picolisp ) 27 28 ;;; INFERIOR PICOLISP MODE STUFF 29 ;;;============================================================================ 30 31 (defconst inferior-picolisp-version "1.2" 32 "Verion-number of library") 33 34 (defcustom inferior-picolisp-mode-hook nil 35 "*Hook for customizing inferior-picolisp mode." 36 :type 'hook 37 :group 'picolisp ) 38 39 (defvar inferior-picolisp-mode-map 40 (let ((m (make-sparse-keymap))) 41 (define-key m "\M-\C-x" 'picolisp-send-definition) ;gnu convention 42 (define-key m "\C-x\C-e" 'picolisp-send-last-sexp) 43 (define-key m "\C-c\C-l" 'picolisp-load-file) 44 m ) ) 45 46 (defvar picolisp-local-program-name "./pil +") 47 (defvar picolisp-process-number 0) 48 49 (defvar picolisp-program-name "pil +" 50 "The name of the program used to run Picolisp." ) 51 52 ;; Install the process communication commands in the picolisp-mode keymap. 53 (define-key picolisp-mode-map "\M-\C-x" 'picolisp-send-definition);gnu convention 54 (define-key picolisp-mode-map "\C-x\C-e" 'picolisp-send-last-sexp);gnu convention 55 (define-key picolisp-mode-map "\C-c\C-e" 'picolisp-send-definition) 56 (define-key picolisp-mode-map "\C-c\M-e" 'picolisp-send-definition-and-go) 57 (define-key picolisp-mode-map "\C-c\C-r" 'picolisp-send-region) 58 (define-key picolisp-mode-map "\C-c\M-r" 'picolisp-send-region-and-go) 59 (define-key picolisp-mode-map "\C-c\C-x" 'switch-to-picolisp) 60 (define-key picolisp-mode-map "\C-c\C-l" 'picolisp-load-file) 61 62 (let ((map (lookup-key picolisp-mode-map [menu-bar picolisp]))) 63 (define-key map [separator-eval] '("--")) 64 (define-key map [load-file] 65 '("Load Picolisp File" . picolisp-load-file) ) 66 (define-key map [switch] 67 '("Switch to Picolisp" . switch-to-picolisp) ) 68 (define-key map [send-def-go] 69 '("Evaluate Last Definition & Go" . picolisp-send-definition-and-go) ) 70 (define-key map [send-def] 71 '("Evaluate Last Definition" . picolisp-send-definition) ) 72 (define-key map [send-region-go] 73 '("Evaluate Region & Go" . picolisp-send-region-and-go) ) 74 (define-key map [send-region] 75 '("Evaluate Region" . picolisp-send-region) ) 76 (define-key map [send-sexp] 77 '("Evaluate Last S-expression" . picolisp-send-last-sexp) ) ) 78 79 (defvar picolisp-buffer) 80 81 (define-derived-mode inferior-picolisp-mode comint-mode "Inferior Picolisp" 82 "Major mode for interacting with an inferior Picolisp process. 83 84 The following commands are available: 85 \\{inferior-picolisp-mode-map} 86 87 An Picolisp process can be fired up with M-x run-picolisp. 88 89 Customization: Entry to this mode runs the hooks on comint-mode-hook and 90 inferior-picolisp-mode-hook (in that order). 91 92 You can send text to the inferior Picolisp process from other buffers containing 93 Picolisp source. 94 switch-to-picolisp switches the current buffer to the Picolisp process buffer. 95 picolisp-send-definition sends the current definition to the Picolisp process. 96 picolisp-send-region sends the current region to the Picolisp process. 97 98 picolisp-send-definition-and-go and picolisp-send-region-and-go 99 switch to the Picolisp process buffer after sending their text. 100 For information on running multiple processes in multiple buffers, see 101 documentation for variable picolisp-buffer. 102 103 Commands: 104 Return after the end of the process' output sends the text from the 105 end of process to point. 106 Return before the end of the process' output copies the sexp ending at point 107 to the end of the process' output, and sends it. 108 Delete converts tabs to spaces as it moves back. 109 Tab indents for Picolisp; with argument, shifts rest 110 of expression rigidly with the current line. 111 C-M-q does Tab on each line starting within following expression. 112 Paragraphs are separated only by blank lines. Semicolons start comments. 113 If you accidentally suspend your process, use \\[comint-continue-subjob] 114 to continue it." 115 ;; Customize in inferior-picolisp-mode-hook 116 (picolisp-mode-variables) 117 (setq comint-prompt-regexp "^[^\n:?!]*[?!:]+ *") 118 (setq comint-prompt-read-only nil) 119 (setq comint-input-filter (function picolisp-input-filter)) 120 (setq comint-get-old-input (function picolisp-get-old-input)) 121 (setq mode-line-process '(":%s")) 122 (setq comint-input-ring-file-name "~/.pil_history") ) 123 124 (defcustom inferior-picolisp-filter-regexp "\\`\\s *\\S ?\\S ?\\s *\\'" 125 "*Input matching this regexp are not saved on the history list. 126 Defaults to a regexp ignoring all inputs of 0, 1, or 2 letters." 127 :type 'regexp 128 :group 'picolisp ) 129 130 (defun picolisp-input-filter (str) 131 "Don't save anything matching `inferior-picolisp-filter-regexp'." 132 (not (string-match inferior-picolisp-filter-regexp str)) ) 133 134 (defun picolisp-get-old-input () 135 "Snarf the sexp ending at point." 136 (save-excursion 137 (let ((end (point))) 138 (backward-sexp) 139 (buffer-substring (point) end) ) ) ) 140 141 (defun picolisp-disable-line-editor () 142 "Disable inbuild PicoLisp line-editor. 143 Not needed when PicoLisp is run as Emacs subprocess." 144 (let ((pil-tmp-dir (expand-file-name "~/.pil/"))) 145 (and (member 146 "editor" (directory-files pil-tmp-dir )) 147 (rename-file 148 (expand-file-name "editor" pil-tmp-dir) 149 (expand-file-name "editor-orig" pil-tmp-dir))) 150 (with-current-buffer 151 (find-file-noselect 152 (expand-file-name "editor" pil-tmp-dir)) 153 (save-buffer) 154 (kill-buffer)))) 155 156 (defun picolisp-reset-line-editor () 157 "Reset inbuild PicoLisp line-editor to original state." 158 (let ((pil-tmp-dir (expand-file-name "~/.pil/"))) 159 (if (member "editor-orig" (directory-files pil-tmp-dir)) 160 (rename-file 161 (expand-file-name "editor-orig" pil-tmp-dir) 162 (expand-file-name "editor" pil-tmp-dir) 163 'OK-IF-ALREADY-EXISTS) 164 (delete-file 165 (expand-file-name "editor" pil-tmp-dir))))) 166 167 168 ;;;###autoload 169 (defun run-picolisp-new-local (cmd) 170 "Run a new inferior Picolisp process for a locally installed 171 PicoLisp, input and output via buffer `*picolisp<N>*'. Works only 172 as intended, when called from inside a picolisp directory, e.g. 173 from a dired buffer showing the top-level directory of a local 174 picolisp installation. Otherwise, calls a global picolisp 175 installation instead (with `picolisp-program-name', see function 176 `picolisp-interactively-start-process'). If there is a process 177 already running in `*picolisp<N>*', create a new process in 178 buffer `*picolisp<N+1>*'. With argument, allows you to edit the 179 command line (default is value of `picolisp-local-program-name'). 180 Runs the hook `inferior-picolisp-mode-hook' \(after the 181 `comint-mode-hook' is run). \(Type \\[describe-mode] in the 182 process buffer for a list of commands.)" 183 184 (interactive (list (if current-prefix-arg 185 (read-string "Run Picolisp: " picolisp-local-program-name) 186 picolisp-local-program-name) ) ) 187 (setq picolisp-process-number (1+ picolisp-process-number)) 188 (setq pl-proc-buf (concat 189 "picolisp<" 190 (number-to-string picolisp-process-number) 191 ">")) 192 (let ((cmdlist (split-string cmd))) 193 (picolisp-disable-line-editor) 194 (set-buffer 195 (apply 'make-comint pl-proc-buf (car cmdlist) 196 nil (cdr cmdlist))) 197 (picolisp-reset-line-editor) 198 (inferior-picolisp-mode) ) 199 (pop-to-buffer (concat "*" pl-proc-buf "*")) ) 200 201 202 ;;;###autoload 203 (defun run-picolisp (cmd) 204 "Run an inferior Picolisp process, input and output via buffer `*picolisp*'. 205 If there is a process already running in `*picolisp*', switch to that buffer. 206 With argument, allows you to edit the command line (default is value 207 of `picolisp-program-name'). 208 Runs the hook `inferior-picolisp-mode-hook' \(after the `comint-mode-hook' 209 is run). 210 \(Type \\[describe-mode] in the process buffer for a list of commands.)" 211 212 (interactive (list (if current-prefix-arg 213 (read-string "Run Picolisp: " picolisp-program-name) 214 picolisp-program-name ) ) ) 215 (when (not (comint-check-proc "*picolisp*")) 216 (let ((cmdlist (split-string cmd))) 217 (picolisp-disable-line-editor) 218 (set-buffer (apply 'make-comint "picolisp" (car cmdlist) 219 nil (cdr cmdlist) ) ) 220 (picolisp-reset-line-editor) 221 (inferior-picolisp-mode) ) ) 222 (setq picolisp-program-name cmd) 223 (setq picolisp-buffer "*picolisp*") 224 (pop-to-buffer "*picolisp*") ) 225 ;;;###autoload (add-hook 'same-window-buffer-names "*picolisp*") 226 227 (defun picolisp-send-region (start end) 228 "Send the current region to the inferior Picolisp process." 229 (interactive "r") 230 (let ((regionsubstring (replace-regexp-in-string "^ 231 " "" (buffer-substring start end) ) ) ) 232 (comint-send-string 233 (picolisp-proc) 234 (if (string= "" (car (last (split-string regionsubstring " 235 " ) ) ) ) 236 regionsubstring 237 (concat regionsubstring "\n") ) ) ) ) 238 239 (defun picolisp-send-definition () 240 "Send the current definition to the inferior Picolisp process." 241 (interactive) 242 (save-excursion 243 (end-of-defun) 244 (let ((end (point))) 245 (beginning-of-defun) 246 (picolisp-send-region 247 (point) (progn (forward-sexp) (point)) ) ) ) ) 248 249 (defun picolisp-send-last-sexp () 250 "Send the previous sexp to the inferior Picolisp process." 251 (interactive) 252 (picolisp-send-region (save-excursion (backward-sexp) (point)) (point)) ) 253 254 (defun switch-to-picolisp (eob-p) 255 "Switch to the picolisp process buffer. 256 With argument, position cursor at end of buffer." 257 (interactive "P") 258 (if (or (and picolisp-buffer (get-buffer picolisp-buffer)) 259 (picolisp-interactively-start-process) ) 260 (pop-to-buffer picolisp-buffer) 261 (error "No current process buffer. See variable `picolisp-buffer'") ) 262 (when eob-p 263 (push-mark) 264 (goto-char (point-max)) ) ) 265 266 (defun picolisp-send-region-and-go (start end) 267 "Send the current region to the inferior Picolisp process. 268 Then switch to the process buffer." 269 (interactive "r") 270 (picolisp-send-region start end) 271 (switch-to-picolisp t) ) 272 273 (defun picolisp-send-definition-and-go () 274 "Send the current definition to the inferior Picolisp. 275 Then switch to the process buffer." 276 (interactive) 277 (picolisp-send-definition) 278 (switch-to-picolisp t) ) 279 280 (defcustom picolisp-source-modes '(picolisp-mode) 281 "*Used to determine if a buffer contains Picolisp source code. 282 If it's loaded into a buffer that is in one of these major modes, 283 it's considered a picolisp source file by `picolisp-load-file'. Used by 284 these commands to determine defaults." 285 :type '(repeat function) 286 :group 'picolisp ) 287 288 (defvar picolisp-prev-load-dir/file nil 289 "Caches the last (directory . file) pair. 290 Caches the last pair used in the last `picolisp-load-file' command. 291 Used for determining the default in the next one." ) 292 293 (defun picolisp-load-file (file-name) 294 "Load a Picolisp file FILE-NAME into the inferior Picolisp process." 295 (interactive (comint-get-source "Load Picolisp file: " picolisp-prev-load-dir/file 296 picolisp-source-modes t ) ) ; t because `load' 297 ; needs an exact name 298 (comint-check-source file-name) ; Check to see if buffer needs saved. 299 (setq picolisp-prev-l/c-dir/file (cons (file-name-directory file-name) 300 (file-name-nondirectory file-name) ) ) 301 (comint-send-string (picolisp-proc) (concat "(load \"" 302 file-name 303 "\"\)\n" ) ) ) 304 305 306 (defvar picolisp-buffer nil "*The current picolisp process buffer. 307 308 MULTIPLE PROCESS SUPPORT 309 =========================================================================== 310 inferior-picolisp.el supports, in a fairly simple fashion, running multiple Picolisp 311 processes. To run multiple Picolisp processes, you start the first up with 312 \\[run-picolisp]. It will be in a buffer named *picolisp*. Rename this buffer 313 with \\[rename-buffer]. You may now start up a new process with another 314 \\[run-picolisp]. It will be in a new buffer, named *picolisp*. You can 315 switch between the different process buffers with \\[switch-to-buffer]. 316 317 Commands that send text from source buffers to Picolisp processes -- like 318 `picolisp-send-definition' -- have to choose a process to send to, when you 319 have more than one Picolisp process around. This is determined by the 320 global variable `picolisp-buffer'. Suppose you have three inferior Picolisps 321 running: 322 Buffer Process 323 foo picolisp 324 bar picolisp<2> 325 *picolisp* picolisp<3> 326 If you do a \\[picolisp-send-definition-and-go] command on some Picolisp source 327 code, what process do you send it to? 328 329 - If you're in a process buffer (foo, bar, or *picolisp*), 330 you send it to that process. 331 - If you're in some other buffer (e.g., a source file), you 332 send it to the process attached to buffer `picolisp-buffer'. 333 This process selection is performed by function `picolisp-proc'. 334 335 Whenever \\[run-picolisp] fires up a new process, it resets `picolisp-buffer' 336 to be the new process's buffer. If you only run one process, this will 337 do the right thing. If you run multiple processes, you can change 338 `picolisp-buffer' to another process buffer with \\[set-variable]. 339 340 More sophisticated approaches are, of course, possible. If you find yourself 341 needing to switch back and forth between multiple processes frequently, 342 you may wish to consider ilisp.el, a larger, more sophisticated package 343 for running inferior Lisp and Picolisp processes. The approach taken here is 344 for a minimal, simple implementation. Feel free to extend it." ) 345 346 (defun picolisp-proc () 347 "Return the current Picolisp process, starting one if necessary. 348 See variable `picolisp-buffer'." 349 (unless (and picolisp-buffer 350 (get-buffer picolisp-buffer) 351 (comint-check-proc picolisp-buffer) ) 352 (picolisp-interactively-start-process) ) 353 (or (picolisp-get-process) 354 (error "No current process. See variable `picolisp-buffer'") ) ) 355 356 (defun picolisp-get-process () 357 "Return the current Picolisp process or nil if none is running." 358 (get-buffer-process (if (eq major-mode 'inferior-picolisp-mode) 359 (current-buffer) 360 picolisp-buffer ) ) ) 361 362 (defun picolisp-interactively-start-process (&optional cmd) 363 "Start an inferior Picolisp process. Return the process started. 364 Since this command is run implicitly, always ask the user for the 365 command to run." 366 (save-window-excursion 367 (run-picolisp (read-string "Run Picolisp: " picolisp-program-name)) ) ) 368 369 ;;; Do the user's customization... 370 371 (defcustom inferior-picolisp-load-hook nil 372 "This hook is run when inferior-picolisp is loaded in. 373 This is a good place to put keybindings." 374 :type 'hook 375 :group 'picolisp ) 376 377 (run-hooks 'inferior-picolisp-load-hook) 378 379 (provide 'inferior-picolisp) 380