-;;; sml-prog-proc.el --- Interacting from a source buffer with an inferior process -*- lexical-binding: t; coding: utf-8 -*-
+;;; prog-proc.el --- Interacting from a source buffer with an inferior process -*- lexical-binding: t; coding: utf-8 -*-
;; Copyright (C) 1999,2000,2003,2004,2005,2007,2012 Stefan Monnier
;; Copyright (C) 1994-1997 Matthew J. Morley
(require 'comint)
(require 'compile)
-(defvar sml-prog-proc-mode-map
+(defvar prog-proc-mode-map
(let ((map (make-sparse-keymap)))
- (define-key map [?\C-c ?\C-l] 'sml-prog-proc-load-file)
- (define-key map [?\C-c ?\C-c] 'sml-prog-proc-compile)
- (define-key map [?\C-c ?\C-z] 'sml-prog-proc-switch-to)
- (define-key map [?\C-c ?\C-r] 'sml-prog-proc-send-region)
- (define-key map [?\C-c ?\C-b] 'sml-prog-proc-send-buffer)
+ (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)
map)
- "Keymap for `sml-prog-proc-mode'.")
+ "Keymap for `prog-proc-mode'.")
-(defvar sml-prog-proc--buffer nil
+(defvar prog-proc--buffer nil
"The inferior-process buffer to which to send code.")
-(make-variable-buffer-local 'sml-prog-proc--buffer)
+(make-variable-buffer-local 'prog-proc--buffer)
-(defstruct (sml-prog-proc-functions
- (:constructor sml-prog-proc-make)
+(defstruct (prog-proc-functions
+ (:constructor prog-proc-make)
(:predicate nil)
(:copier nil))
(name :read-only t)
(load-cmd :read-only t)
(chdir-cmd :read-only t))
-(defvar sml-prog-proc-functions nil
+(defvar prog-proc-functions nil
"Struct containing the various functions to create a new process, ...")
-(defmacro sml-prog-proc--call (method &rest args)
- `(sml-prog-proc--funcall
- #',(intern (format "sml-prog-proc-functions-%s" method))
+(defmacro prog-proc--call (method &rest args)
+ `(prog-proc--funcall
+ #',(intern (format "prog-proc-functions-%s" method))
,@args))
-(defun sml-prog-proc--funcall (selector &rest args)
- (if (not sml-prog-proc-functions)
+(defun prog-proc--funcall (selector &rest args)
+ (if (not prog-proc-functions)
;; FIXME: Look for available ones and pick one.
- (error "Not an `sml-prog-proc' buffer")
- (apply (funcall selector sml-prog-proc-functions) args)))
+ (error "Not an `prog-proc' buffer")
+ (apply (funcall selector prog-proc-functions) args)))
;; The inferior process and his buffer are basically interchangeable.
-;; Currently the code takes sml-prog-proc--buffer as the main reference,
-;; but all users should either use sml-prog-proc-proc or sml-prog-proc-buffer
+;; 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 sml-prog-proc-proc ()
+(defun prog-proc-proc ()
"Return the inferior process for the code in current buffer."
- (or (and (buffer-live-p sml-prog-proc--buffer)
- (get-buffer-process sml-prog-proc--buffer))
- (when (derived-mode-p 'sml-prog-proc-mode 'sml-prog-proc-comint-mode)
- (setq sml-prog-proc--buffer (current-buffer))
- (get-buffer-process sml-prog-proc--buffer))
- (let ((buf (sml-prog-proc--call run)))
- (setq sml-prog-proc--buffer buf)
- (get-buffer-process sml-prog-proc--buffer))))
-
-(defun sml-prog-proc-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 ((buf (prog-proc--call run)))
+ (setq prog-proc--buffer buf)
+ (get-buffer-process prog-proc--buffer))))
+
+(defun prog-proc-buffer ()
"Return the buffer of the inferior process."
- (process-buffer (sml-prog-proc-proc)))
+ (process-buffer (prog-proc-proc)))
-(defun sml-prog-proc-switch-to ()
+(defun prog-proc-switch-to ()
"Switch to the buffer running the read-eval-print process."
- (pop-to-buffer (sml-prog-proc-buffer)))
+ (pop-to-buffer (prog-proc-buffer)))
-(defun sml-prog-proc-send-string (proc str)
+(defun prog-proc-send-string (proc str)
(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?
- (sml-prog-proc-comint-input-filter-function nil)
+ (prog-proc-comint-input-filter-function nil)
(comint-send-string proc (concat str "\n"))))
-(defun sml-prog-proc-load-file (file &optional and-go)
+(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
(read-file-name "File to load: " nil nil t))
current-prefix-arg))
(comint-check-source file)
- (let ((proc (sml-prog-proc-proc)))
- (sml-prog-proc-send-string proc (sml-prog-proc--call load-cmd 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 sml-prog-proc--tmp-file nil)
+(defvar prog-proc--tmp-file nil)
-(defun sml-prog-proc-send-region (start end &optional and-go)
+(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 (sml-prog-proc-proc))
+ (let ((proc (prog-proc-proc))
(tmp (make-temp-file "emacs-region")))
(write-region start end tmp nil 'silently)
- (when sml-prog-proc--tmp-file
- (ignore-errors (delete-file (car sml-prog-proc--tmp-file)))
- (set-marker (cdr sml-prog-proc--tmp-file) nil))
- (setq sml-prog-proc--tmp-file (cons tmp (copy-marker start)))
- (sml-prog-proc-send-string proc (sml-prog-proc--call load-cmd tmp))
+ (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 sml-prog-proc-send-buffer (&optional and-go)
+(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")
- (sml-prog-proc-send-region (point-min) (point-max) and-go))
+ (prog-proc-send-region (point-min) (point-max) and-go))
;; FIXME: How 'bout a menu? Now, that's trickier because keymap inheritance
;; doesn't play nicely with menus!
-(define-derived-mode sml-prog-proc-mode prog-mode "Prog-Proc"
+(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 sml-prog-proc-chdir (dir)
+(defun prog-proc-chdir (dir)
"Change the working directory of the inferior process."
(interactive "DChange to directory: ")
(let ((dir (expand-file-name dir))
- (proc (sml-prog-proc-proc)))
+ (proc (prog-proc-proc)))
(with-current-buffer (process-buffer proc)
- (sml-prog-proc-send-string proc (sml-prog-proc--call chdir-cmd dir))
+ (prog-proc-send-string proc (prog-proc--call chdir-cmd dir))
(setq default-directory (file-name-as-directory dir)))))
-(defun sml-prog-proc-comint-input-filter-function (str)
+(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 sml-prog-proc--tmp-file
- (compilation-fake-loc (cdr sml-prog-proc--tmp-file)
- (car sml-prog-proc--tmp-file)))
+ (if prog-proc--tmp-file
+ (compilation-fake-loc (cdr prog-proc--tmp-file)
+ (car prog-proc--tmp-file)))
str)
-(define-derived-mode sml-prog-proc-comint-mode comint-mode "Prog-Proc-Comint"
+(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
(cons 'compilation-minor-mode map)))
(add-hook 'comint-input-filter-functions
- #'sml-prog-proc-comint-input-filter-function nil t))
+ #'prog-proc-comint-input-filter-function nil t))
-(provide 'sml-prog-proc)
-;;; sml-prog-proc.el ends here
+(provide 'prog-proc)
+;;; prog-proc.el ends here
(eval-when-compile (require 'cl))
(require 'smie nil 'noerror)
-(require 'sml-prog-proc)
+(require 'electric)
+(require 'prog-proc)
(defgroup sml ()
"Editing SML code."
and b = B and b = B"
:type 'boolean)
+(defcustom sml-electric-pipe-mode t
+ "If non-nil, automatically insert appropriate template when hitting |."
+ :type 'boolean)
+
(defvar sml-mode-hook nil
"Run upon entering `sml-mode'.
This is a good place to put your preferred key bindings.")
"The starters of new expressions.")
(defconst sml-pipeheads
- '("|" "of" "fun" "fn" "and" "handle" "datatype" "abstype")
+ '("|" "of" "fun" "fn" "and" "handle" "datatype" "abstype"
+ "(" "{" "[")
"A `|' corresponds to one of these.")
(defconst sml-keywords-regexp
(defconst sml-id-re "\\sw\\(?:\\sw\\|\\s_\\)*"))
(defconst sml-tyvarseq-re
- (concat "\\(\\('+" sml-id-re "\\|(\\([,']\\|" sml-id-re
+ (concat "\\(?:\\(?:'+" sml-id-re "\\|(\\(?:[,']\\|" sml-id-re
"\\|\\s-\\)+)\\)\\s-+\\)?"))
;;; Font-lock settings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(,(concat "\\_<\\(fun\\|and\\)\\s-+" sml-tyvarseq-re
"\\(" sml-id-re "\\)\\s-+[^ \t\n=]")
(1 font-lock-keyword-face)
- (6 font-lock-function-name-face))
- (,(concat "\\_<\\(\\(data\\|abs\\|with\\|eq\\)?type\\)\\s-+"
+ (2 font-lock-function-name-face))
+ (,(concat "\\_<\\(\\(?:data\\|abs\\|with\\|eq\\)?type\\)\\s-+"
sml-tyvarseq-re "\\(" sml-id-re "\\)")
(1 font-lock-keyword-face)
- (7 font-lock-type-def-face))
- (,(concat "\\_<\\(val\\)\\s-+\\(" sml-id-re "\\_>\\s-*\\)?\\("
+ (2 font-lock-type-def-face))
+ (,(concat "\\_<\\(val\\)\\s-+\\(?:" sml-id-re "\\_>\\s-*\\)?\\("
sml-id-re "\\)\\s-*[=:]")
(1 font-lock-keyword-face)
- ;;(6 font-lock-variable-def-face nil t)
- (3 font-lock-variable-name-face))
+ (2 font-lock-variable-name-face))
(,(concat "\\_<\\(structure\\|functor\\|abstraction\\)\\s-+\\("
sml-id-re "\\)")
(1 font-lock-keyword-face)
See `compilation-error-regexp-alist' for a description of the format.")
(defconst sml-pp-functions
- (sml-prog-proc-make :name "SML"
- :run (lambda () (call-interactively #'sml-run))
- :load-cmd (lambda (file)
- ;; `sml-use-command' was defined a long time
- ;; ago not to include a final semi-colon.
- (concat (format sml-use-command file) ";"))
- :chdir-cmd (lambda (dir)
- ;; `sml-cd-command' was defined a long time
- ;; ago not to include a final semi-colon.
- (concat (format sml-cd-command dir) ";"))))
+ (prog-proc-make :name "SML"
+ :run (lambda () (call-interactively #'sml-run))
+ :load-cmd (lambda (file)
+ ;; `sml-use-command' was defined a long time
+ ;; ago not to include a final semi-colon.
+ (concat (format sml-use-command file) ";"))
+ :chdir-cmd (lambda (dir)
+ ;; `sml-cd-command' was defined a long time
+ ;; ago not to include a final semi-colon.
+ (concat (format sml-cd-command dir) ";"))))
;; font-lock support
(defconst inferior-sml-font-lock-keywords
(smerge-refine-subst b1 e1 b2 e2
'((face . smerge-refined-change))))))))))
-(define-derived-mode inferior-sml-mode sml-prog-proc-comint-mode "Inferior-SML"
+(define-derived-mode inferior-sml-mode prog-proc-comint-mode "Inferior-SML"
"Major mode for interacting with an inferior ML process.
The following commands are available:
(cmd "cd \"."))
;; Look for files to determine the default command.
(while (and (stringp dir)
- (dolist (cf sml-compile-commands-alist 1)
- (when (file-exists-p (expand-file-name (cdr cf) dir))
- (setq cmd (concat cmd "\"; " (car cf))) (return nil))))
+ (progn
+ (dolist (cf sml-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 "/.."))))
(add-to-list 'auto-mode-alist '("\\.s\\(ml\\|ig\\)\\'" . sml-mode))
(defvar comment-quote-nested)
-(defvar electric-indent-chars)
-(defvar electric-layout-rules)
;;;###autoload
-(define-derived-mode sml-mode sml-prog-proc-mode "SML"
+(define-derived-mode sml-mode prog-proc-mode "SML"
"\\<sml-mode-map>Major mode for editing Standard ML code.
This mode runs `sml-mode-hook' just before exiting.
See also (info \"(sml-mode)Top\").
\\{sml-mode-map}"
- (set (make-local-variable 'sml-prog-proc-functions) sml-pp-functions)
+ (set (make-local-variable 'prog-proc-functions) sml-pp-functions)
(set (make-local-variable 'font-lock-defaults) sml-font-lock-defaults)
(set (make-local-variable 'outline-regexp) sml-outline-regexp)
(set (make-local-variable 'imenu-create-index-function)
(progn (skip-chars-forward " \t;")
(eolp)))
'after))))))
+ (when sml-electric-pipe-mode
+ (add-hook 'post-self-insert-hook #'sml-post-self-insert-pipe nil t))
(sml-mode-variables))
(defun sml-mode-variables ()
(not (looking-at re)))
(or (ignore-errors (forward-sexp 1) t) (forward-char 1))))
-(defun sml-electric-pipe () ;FIXME: Use post-self-insert-hook?
+(defun sml-electric-pipe ()
"Insert a \"|\".
Depending on the context insert the name of function, a \"=>\" etc."
- ;; FIXME: Make it a skeleton.
(interactive)
(unless (save-excursion (skip-chars-backward "\t ") (bolp)) (insert "\n"))
(insert "| ")
- (let ((text
- (save-excursion
- (backward-char 2) ;back over the just inserted "| "
- (let ((sym (sml-find-matching-starter sml-pipeheads
- ;; (sml-op-prec "|" 'back)
- )))
- (sml-smie-forward-token)
- (forward-comment (point-max))
- (cond
- ((string= sym "|")
- (let ((f (sml-smie-forward-token)))
- (sml-find-forward "\\(=>\\|=\\||\\)\\S.")
- (cond
- ((looking-at "|") "") ;probably a datatype
- ((looking-at "=>") " => ") ;`case', or `fn' or `handle'
- ((looking-at "=") (concat f " = "))))) ;a function
- ((string= sym "and")
- ;; could be a datatype or a function
- (setq sym (sml-funname-of-and))
- (if sym (concat sym " = ") ""))
- ;; trivial cases
- ((string= sym "fun")
- (while (and (setq sym (sml-smie-forward-token))
- (string-match "^'" sym))
- (forward-comment (point-max)))
- (concat sym " = "))
- ((member sym '("case" "handle" "fn" "of")) " => ")
- ;;((member sym '("abstype" "datatype")) "")
- (t ""))))))
-
- (insert text)
- (indent-according-to-mode)
- (beginning-of-line)
- (skip-chars-forward "\t |")
- (skip-syntax-forward "w_")
- (skip-chars-forward "\t ")
- (when (eq ?= (char-after)) (backward-char))))
+ (unless (sml-post-self-insert-pipe (1- (point)))
+ (indent-according-to-mode)))
+
+(defun sml-post-self-insert-pipe (&optional acp)
+ (when (or acp (and (eq ?| last-command-event)
+ (setq acp (electric--after-char-pos))))
+ (let ((text
+ (save-excursion
+ (goto-char (1- acp)) ;Jump before the "|" we just inserted.
+ (let ((sym (sml-find-matching-starter sml-pipeheads
+ ;; (sml-op-prec "|" 'back)
+ )))
+ (sml-smie-forward-token)
+ (forward-comment (point-max))
+ (cond
+ ((string= sym "|")
+ (let ((f (sml-smie-forward-token)))
+ (sml-find-forward "\\(=>\\|=\\||\\)\\S.")
+ (cond
+ ((looking-at "|") nil) ; A datatype or an OR pattern?
+ ((looking-at "=>") " => ") ;`case', or `fn' or `handle'.
+ ((looking-at "=") ;A function.
+ (cons (concat f " ")" = ")))))
+ ((string= sym "and")
+ ;; Could be a datatype or a function.
+ (let ((funname (sml-funname-of-and)))
+ (if funname (cons (concat funname " ") " = ") nil)))
+ ((string= sym "fun")
+ (while (and (setq sym (sml-smie-forward-token))
+ (string-match "^'" sym))
+ (forward-comment (point-max)))
+ (cons (concat sym " ") " = "))
+ ((member sym '("case" "handle" "of")) " => ") ;; "fn"?
+ ;;((member sym '("abstype" "datatype")) "")
+ (t nil))))))
+ (when text
+ (save-excursion
+ (goto-char (1- acp))
+ (unless (save-excursion (skip-chars-backward "\t ") (bolp))
+ (insert "\n")))
+ (unless (memq (char-before) '(?\s ?\t)) (insert " "))
+ (let ((use-region (and (use-region-p) (< (point) (mark)))))
+ ;; (skeleton-proxy-new `(nil ,(if (consp text) (pop text)) _ ,text))
+ (when (consp text) (insert (pop text)))
+ (if (not use-region)
+ (save-excursion (insert text))
+ (goto-char (mark))
+ (insert text)))
+ (indent-according-to-mode)
+ t))))
+
;;; Misc