From: "Elias Mårtenson" <lokedhs@gmail.com>
To: Stefan Monnier <monnier@iro.umontreal.ca>
Cc: Sam Steingold <sds@gnu.org>, emacs-devel@gnu.org
Subject: Re: over-engineered (and under-standardized) inferior interfaces
Date: Mon, 1 Sep 2014 00:30:41 +0800 [thread overview]
Message-ID: <CADtN0WKRmjGp44m-bCs7G1_-1fT4rBDM6D=RybzeH7D=zQAKYw@mail.gmail.com> (raw)
In-Reply-To: <jwvvbpld20s.fsf-monnier+emacs@gnu.org>
[-- Attachment #1: Type: text/plain, Size: 13521 bytes --]
I'll take a look at this for gnu-apl-mode, if you can accept getting
questions from me. :-)
Regards,
Elias
On 22 August 2014 04:29, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
> > 2. Lack of standardization in interaction and keybindings.
>
> 100% agreement. FWIW, I have started a `prog-proc-mode', which is
> supposed to be a minor mode used in a programming mode and that makes
> the link to an underlying comint mode.
>
> It probably doesn't address all your concerns, but the main motivation
> was to try and consolidate/unify all the various key-bindings.
> It's currently in use in sml-mode (tho copied and renamed to
> sml-prog-proc-mode).
>
> If someone were to try and make use of it in some other major-mode than
> sml-mode (which may require making additions to prog-proc-mode since
> sml-mode's original support for interaction with an inferior process was
> not particularly sophisticated), that would be great.
>
> Stefan
>
>
> ;;; prog-proc.el --- Interacting from a source buffer with an inferior
> process -*- lexical-binding: t -*-
>
> ;; Copyright (C) 2012 Free Software Foundation, Inc.
>
> ;; Author: (Stefan Monnier) <monnier@iro.umontreal.ca>
>
> ;; 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
>
>
[-- Attachment #2: Type: text/html, Size: 16229 bytes --]
next prev parent reply other threads:[~2014-08-31 16:30 UTC|newest]
Thread overview: 46+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-08-21 19:20 over-engineered (and under-standardized) inferior interfaces Sam Steingold
2014-08-21 20:29 ` Stefan Monnier
2014-08-21 21:21 ` Jorgen Schaefer
2014-08-21 22:00 ` Dmitry
2014-08-21 22:10 ` Eric S. Raymond
2014-08-22 3:45 ` Stefan Monnier
2014-08-22 5:57 ` Andreas Röhler
2014-08-22 10:55 ` Phillip Lord
2014-08-22 11:41 ` Jorgen Schaefer
2014-08-22 21:29 ` Sam Steingold
2014-08-31 16:30 ` Elias Mårtenson [this message]
2014-08-31 20:01 ` Stefan Monnier
2014-08-21 20:43 ` Andreas Schwab
2014-08-21 21:06 ` Sam Steingold
2014-08-22 5:56 ` Andreas Schwab
2014-08-22 6:17 ` Michael Albinus
2014-08-22 21:25 ` Sam Steingold
2014-08-23 15:16 ` Michael Albinus
2014-08-25 17:05 ` Sam Steingold
2014-08-27 6:57 ` Michael Albinus
2014-08-27 10:46 ` Sam Steingold
2014-08-27 11:29 ` Michael Albinus
2014-08-27 12:32 ` Sam Steingold
2014-08-27 12:51 ` Michael Albinus
2014-08-27 13:00 ` Sam Steingold
2014-08-27 13:16 ` Michael Albinus
2014-08-27 13:52 ` Sam Steingold
2014-08-27 13:57 ` Sam Steingold
2014-08-27 18:17 ` Michael Albinus
2014-08-28 1:01 ` Sam Steingold
2014-08-28 8:48 ` Michael Albinus
2014-08-28 16:02 ` Sam Steingold
2014-08-28 18:35 ` Michael Albinus
2014-08-28 19:22 ` Sam Steingold
2014-08-28 19:44 ` Michael Albinus
2014-08-28 19:54 ` Sam Steingold
2014-08-28 20:04 ` Michael Albinus
2014-08-29 15:00 ` Sam Steingold
2014-09-05 14:19 ` Michael Albinus
2014-09-08 13:17 ` Sam Steingold
2014-09-08 14:15 ` Michael Albinus
2014-09-08 14:38 ` Sam Steingold
2014-09-08 14:47 ` Michael Albinus
2014-08-27 14:05 ` Tassilo Horn
2014-08-27 18:13 ` Michael Albinus
2014-08-25 9:23 ` Michael Mattie
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='CADtN0WKRmjGp44m-bCs7G1_-1fT4rBDM6D=RybzeH7D=zQAKYw@mail.gmail.com' \
--to=lokedhs@gmail.com \
--cc=emacs-devel@gnu.org \
--cc=monnier@iro.umontreal.ca \
--cc=sds@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.