picolisp

Unnamed repository; edit this file to name it for gitweb.
git clone https://logand.com/git/picolisp.git/
Log | Files | Refs | README | LICENSE

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