Glenn Morris <rgm@HIDDEN>
to control <at> debbugs.gnu.org
.
Full text available.Received: (at quiet) by debbugs.gnu.org; 24 Oct 2013 07:24:46 +0000 From debbugs-submit-bounces <at> debbugs.gnu.org Thu Oct 24 03:24:46 2013 Received: from localhost ([127.0.0.1]:40075 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>) id 1VZFHf-00036W-QF for submit <at> debbugs.gnu.org; Thu, 24 Oct 2013 03:24:45 -0400 Received: from fencepost.gnu.org ([208.118.235.10]:46116 ident=Debian-exim) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from <rgm@HIDDEN>) id 1VZFHa-00036H-ME for quiet <at> debbugs.gnu.org; Thu, 24 Oct 2013 03:24:40 -0400 Received: from rgm by fencepost.gnu.org with local (Exim 4.71) (envelope-from <rgm@HIDDEN>) id 1VZFHZ-0007KO-Di; Thu, 24 Oct 2013 03:24:37 -0400 From: Glenn Morris <rgm@HIDDEN> To: quiet <at> debbugs.gnu.org Subject: library for "standard" link between a programming mode buffer and an inferior process References: <20131012170846.GA688@HIDDEN> <ntvc0ux9g4.fsf@HIDDEN> <20131022005606.GA10573@HIDDEN> <jwvwql52y82.fsf-monnier+emacsbugs@HIDDEN> X-Spook: csim George W. Bush 9705 Samford Road world domination X-Ran: I1T}.md0'b.plclF`RBT(Du4IFn$MLYOJPR<IF4<R5y|!a<t3RL@P5AB55nW[IJ^cC!&I5 X-Hue: red X-Debbugs-No-Ack: yes X-Attribution: GM Date: Thu, 24 Oct 2013 03:24:37 -0400 In-Reply-To: <jwvwql52y82.fsf-monnier+emacsbugs@HIDDEN> (Stefan Monnier's message of "Tue, 22 Oct 2013 10:23:21 -0400") Message-ID: <lh1u3bf7wq.fsf_-_@HIDDEN> User-Agent: Gnus (www.gnus.org), GNU Emacs (www.gnu.org/software/emacs/) MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Spam-Score: -5.4 (-----) X-Debbugs-Envelope-To: quiet X-BeenThere: debbugs-submit <at> debbugs.gnu.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: <debbugs-submit.debbugs.gnu.org> List-Unsubscribe: <http://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe> List-Archive: <http://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/> List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org> List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help> List-Subscribe: <http://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe> Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org> X-Spam-Score: -5.4 (-----) Package: emacs Severity: wishlist Split from http://debbugs.gnu.org/cgi/bugreport.cgi?bug=15599#20 From: Stefan Monnier <monnier <at> iro.umontreal.ca> Date: Tue, 22 Oct 2013 10:23:21 -0400 BTW, regarding inferior processes, it'd be really good to add a library that provides the "standard" link between a programming mode buffer and an inferior process, and then to adapt existing major modes to use that library. FWIW, I had started such a library (while working on sml-mode) but it's just a first attempt, which has not yet been used for anything else than sml-mode. You can check sml-mode.el (in elpa) to see an example of its use (tho sml-mode includes a copy of that prog-proc thingy, renamed to sml-prog-proc). Stefan ;;; prog-proc.el --- Interacting from a source buffer with an inferior process -*- lexical-binding: t; coding: utf-8 -*- ;; Copyright (C) 2012 Free Software Foundation, Inc. ;; Author: (Stefan Monnier) <monnier@HIDDEN> ;; This file is part of GNU Emacs. ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;; Prog-Proc is a package designed to complement Comint: while Comint was ;; designed originally to handle the needs of inferior process buffers, such ;; as a buffer running a Scheme repl, Comint does not actually provide any ;; functionality that links this process buffer with some source code. ;; ;; That's where Prog-Proc comes into play: it provides the usual commands and ;; key-bindings that lets the user send his code to the underlying repl. ;;; Code: (eval-when-compile (require 'cl)) (require 'comint) (require 'compile) (defvar prog-proc-mode-map (let ((map (make-sparse-keymap))) (define-key map [?\C-c ?\C-l] 'prog-proc-load-file) (define-key map [?\C-c ?\C-c] 'prog-proc-compile) (define-key map [?\C-c ?\C-z] 'prog-proc-switch-to) (define-key map [?\C-c ?\C-r] 'prog-proc-send-region) (define-key map [?\C-c ?\C-b] 'prog-proc-send-buffer) ;; FIXME: Add ;; (define-key map [?\M-C-x] 'prog-proc-send-defun) ;; (define-key map [?\C-x ?\C-e] 'prog-proc-send-last-sexp) ;; FIXME: Add menu. Now, that's trickier because keymap inheritance ;; doesn't play nicely with menus! map) "Keymap for `prog-proc-mode'.") (defvar prog-proc--buffer nil "The inferior-process buffer to which to send code.") (make-variable-buffer-local 'prog-proc--buffer) (defstruct (prog-proc-descriptor (:constructor prog-proc-make) (:predicate nil) (:copier nil)) (name nil :read-only t) (run nil :read-only t) (load-cmd nil :read-only t) (chdir-cmd nil :read-only t) (command-eol "\n" :read-only t) (compile-commands-alist nil :read-only t)) (defvar prog-proc-descriptor nil "Struct containing the various functions to create a new process, ...") (defmacro prog-proc--prop (prop) `(,(intern (format "prog-proc-descriptor-%s" prop)) (or prog-proc-descriptor ;; FIXME: Look for available ones and pick one. (error "Not a `prog-proc' buffer")))) (defmacro prog-proc--call (method &rest args) `(funcall (prog-proc--prop ,method) ,@args)) ;; The inferior process and his buffer are basically interchangeable. ;; Currently the code takes prog-proc--buffer as the main reference, ;; but all users should either use prog-proc-proc or prog-proc-buffer ;; to find the info. (defun prog-proc-proc () "Return the inferior process for the code in current buffer." (or (and (buffer-live-p prog-proc--buffer) (get-buffer-process prog-proc--buffer)) (when (derived-mode-p 'prog-proc-mode 'prog-proc-comint-mode) (setq prog-proc--buffer (current-buffer)) (get-buffer-process prog-proc--buffer)) (let ((ppd prog-proc-descriptor) (buf (prog-proc--call run))) (with-current-buffer buf (if (and ppd (null prog-proc-descriptor)) (set (make-local-variable 'prog-proc-descriptor) ppd))) (setq prog-proc--buffer buf) (get-buffer-process prog-proc--buffer)))) (defun prog-proc-buffer () "Return the buffer of the inferior process." (process-buffer (prog-proc-proc))) (defun prog-proc-run-repl () "Start the read-eval-print process, if it's not running yet." (interactive) (ignore (prog-proc-proc))) (defun prog-proc-switch-to () "Switch to the buffer running the read-eval-print process." (pop-to-buffer (prog-proc-buffer))) (defun prog-proc-send-string (proc str) "Send command STR to PROC, with an EOL terminator appended." (with-current-buffer (process-buffer proc) ;; FIXME: comint-send-string does not pass the string through ;; comint-input-filter-function, so we have to do it by hand. ;; Maybe we should insert the command into the buffer and then call ;; comint-send-input? (prog-proc-comint-input-filter-function nil) (comint-send-string proc (concat str (prog-proc--prop command-eol))))) (defun prog-proc-load-file (file &optional and-go) "Load FILE into the read-eval-print process. FILE is the file visited by the current buffer. If prefix argument AND-GO is used, then we additionally switch to the buffer where the process is running." (interactive (list (or buffer-file-name (read-file-name "File to load: " nil nil t)) current-prefix-arg)) (comint-check-source file) (let ((proc (prog-proc-proc))) (prog-proc-send-string proc (prog-proc--call load-cmd file)) (when and-go (pop-to-buffer (process-buffer proc))))) (defvar prog-proc--tmp-file nil) (defun prog-proc-send-region (start end &optional and-go) "Send the content of the region to the read-eval-print process. START..END delimit the region; AND-GO if non-nil indicate to additionally switch to the process's buffer." (interactive "r\nP") (if (> start end) (let ((tmp end)) (setq end start) (setq start tmp)) (if (= start end) (error "Nothing to send: the region is empty"))) (let ((proc (prog-proc-proc)) (tmp (make-temp-file "emacs-region"))) (write-region start end tmp nil 'silently) (when prog-proc--tmp-file (ignore-errors (delete-file (car prog-proc--tmp-file))) (set-marker (cdr prog-proc--tmp-file) nil)) (setq prog-proc--tmp-file (cons tmp (copy-marker start))) (prog-proc-send-string proc (prog-proc--call load-cmd tmp)) (when and-go (pop-to-buffer (process-buffer proc))))) (defun prog-proc-send-buffer (&optional and-go) "Send the content of the current buffer to the read-eval-print process. AND-GO if non-nil indicate to additionally switch to the process's buffer." (interactive "P") (prog-proc-send-region (point-min) (point-max) and-go)) (define-derived-mode prog-proc-mode prog-mode "Prog-Proc" "Major mode for editing source code and interact with an interactive loop." ) ;;; Extended comint-mode for Prog-Proc. (defun prog-proc-chdir (dir) "Change the working directory of the inferior process to DIR." (interactive "DChange to directory: ") (let ((dir (expand-file-name dir)) (proc (prog-proc-proc))) (with-current-buffer (process-buffer proc) (prog-proc-send-string proc (prog-proc--call chdir-cmd dir)) (setq default-directory (file-name-as-directory dir))))) (defun prog-proc-comint-input-filter-function (str) ;; `compile.el' doesn't know that file location info from errors should be ;; recomputed afresh (without using stale info from earlier compilations). (compilation-forget-errors) ;Has to run before compilation-fake-loc. (if (and prog-proc--tmp-file (marker-buffer (cdr prog-proc--tmp-file))) (compilation-fake-loc (cdr prog-proc--tmp-file) (car prog-proc--tmp-file))) str) (defvar prog-proc-comint-mode-map (let ((map (make-sparse-keymap))) (define-key map [?\C-c ?\C-r] 'prog-proc-run-repl) (define-key map [?\C-c ?\C-l] 'prog-proc-load-file) map)) (define-derived-mode prog-proc-comint-mode comint-mode "Prog-Proc-Comint" "Major mode for an inferior process used to run&compile source code." ;; Enable compilation-minor-mode, but only after the child mode is setup ;; since the child-mode might want to add rules to ;; compilation-error-regexp-alist. (add-hook 'after-change-major-mode-hook #'compilation-minor-mode nil t) ;; The keymap of compilation-minor-mode is too unbearable, so we ;; need to hide most of the bindings. (let ((map (make-sparse-keymap))) (dolist (keys '([menu-bar] [follow-link])) ;; Preserve some of the bindings. (define-key map keys (lookup-key compilation-minor-mode-map keys))) (add-to-list 'minor-mode-overriding-map-alist (cons 'compilation-minor-mode map))) (add-hook 'comint-input-filter-functions #'prog-proc-comint-input-filter-function nil t)) (defvar prog-proc--compile-command nil "The command used by default by `prog-proc-compile'.") (defun prog-proc-compile (command &optional and-go) "Pass COMMAND to the read-eval-loop process to compile the current file. You can then use the command \\[next-error] to find the next error message and move to the source code that caused it. Interactively, prompts for the command if `compilation-read-command' is non-nil. With prefix arg, always prompts. Prefix arg AND-GO also means to switch to the read-eval-loop buffer afterwards." (interactive (let* ((dir default-directory) (cmd "cd \".")) ;; Look for files to determine the default command. (while (and (stringp dir) (progn (dolist (cf (prog-proc--prop compile-commands-alist)) (when (file-exists-p (expand-file-name (cdr cf) dir)) (setq cmd (concat cmd "\"; " (car cf))) (return nil))) (not cmd))) (let ((newdir (file-name-directory (directory-file-name dir)))) (setq dir (unless (equal newdir dir) newdir)) (setq cmd (concat cmd "/..")))) (setq cmd (cond ((local-variable-p 'prog-proc--compile-command) prog-proc--compile-command) ((string-match "^\\s-*cd\\s-+\"\\.\"\\s-*;\\s-*" cmd) (substring cmd (match-end 0))) ((string-match "^\\s-*cd\\s-+\"\\(\\./\\)" cmd) (replace-match "" t t cmd 1)) ((string-match ";" cmd) cmd) (t prog-proc--compile-command))) ;; code taken from compile.el (list (if (or compilation-read-command current-prefix-arg) (read-from-minibuffer "Compile command: " cmd nil nil '(compile-history . 1)) cmd)))) ;; ;; now look for command's file to determine the directory ;; (setq dir default-directory) ;; (while (and (stringp dir) ;; (dolist (cf (prog-proc--prop compile-commands-alist) t) ;; (when (and (equal cmd (car cf)) ;; (file-exists-p (expand-file-name (cdr cf) dir))) ;; (return nil)))) ;; (let ((newdir (file-name-directory (directory-file-name dir)))) ;; (setq dir (unless (equal newdir dir) newdir)))) ;; (setq dir (or dir default-directory)) ;; (list cmd dir))) (set (make-local-variable 'prog-proc--compile-command) command) (save-some-buffers (not compilation-ask-about-save) nil) (let ((dir default-directory)) (when (string-match "^\\s-*cd\\s-+\"\\([^\"]+\\)\"\\s-*;" command) (setq dir (match-string 1 command)) (setq command (replace-match "" t t command))) (setq dir (expand-file-name dir)) (let ((proc (prog-proc-proc)) (eol (prog-proc--prop command-eol))) (with-current-buffer (process-buffer proc) (setq default-directory dir) (prog-proc-send-string proc (concat (prog-proc--call chdir-cmd dir) ;; Strip the newline, to avoid adding a prompt. (if (string-match "\n\\'" eol) (replace-match " " t t eol) eol) command)) (when and-go (pop-to-buffer (process-buffer proc))))))) (provide 'prog-proc) ;;; prog-proc.el ends here
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997 nCipher Corporation Ltd,
1994-97 Ian Jackson.