;;; valeri-mode.el --- a major-mode for editing Valeri programs -*- lexical-binding: t -*- (require 'comint) (defcustom valeri-default-application "valeri" "Default application to run in Valeri process." :type 'string :group 'valeri) (defcustom valeri-default-command-switches (list) "Command switches for `valeri-default-application'. Should be a list of strings." :type '(repeat string) :group 'valeri) (defcustom valeri-prompt-regexp "[^\n]*\\(valeri>[\t ]+\\)+$" "Regexp which matches the Valeri program's prompt." :type 'regexp :group 'valeri) (defcustom valeri-traceback-line-re ;; This regexp skips prompt and meaningless "stdin:N:" prefix when looking ;; for actual file-line locations. "^\\(?:[\t ]*\\|.*>[\t ]+\\)\\(?:[^\n\t ]+:[0-9]+:[\t ]*\\)*\\(?:\\([^\n\t ]+\\):\\([0-9]+\\):\\)" "Regular expression that describes tracebacks and errors." :type 'regexp :group 'valeri) (defvar valeri-process-init-code (mapconcat 'identity '("") " ")) (defvar valeri-process nil "The active Valeri process") (defvar valeri-process-buffer nil "Buffer used for communication with the Valeri process.") (defvar valeri--repl-buffer-p nil "Buffer-local flag saying if this is a Valeri REPL buffer.") (make-variable-buffer-local 'valeri--repl-buffer-p) (defun valeri-get-create-process () "Return active Valeri process creating one if necessary." (valeri-start-process) valeri-process) (defun valeri-send-string (str) "Send STR plus a newline to the Valeri process. If `valeri-process' is nil or dead, start a new process first." (unless (string-equal (substring str -1) "\n") (setq str (concat str "\n"))) (process-send-string (valeri-get-create-process) str)) ;;;###autoload (define-derived-mode valeri-mode lisp-mode "Valeri" "Major mode for editing Valeri code." :group 'valeri ) ;;;###autoload (defalias 'run-valeri #'valeri-start-process) ;;;###autoload (defun valeri-start-process (&optional name program startfile &rest switches) "Start a Valeri process named NAME, running PROGRAM. PROGRAM defaults to NAME, which defaults to `valeri-default-application'. When called interactively, switch to the process buffer." (interactive) (setq name (or name valeri-default-application)) (setq program (or program valeri-default-application)) ;; don't re-initialize if there already is a Valeri process (unless (comint-check-proc (format "*%s*" name)) (setq valeri-process-buffer (apply #'make-comint name program startfile (or switches valeri-default-command-switches))) (setq valeri-process (get-buffer-process valeri-process-buffer)) (set-process-query-on-exit-flag valeri-process nil) (with-current-buffer valeri-process-buffer ;; enable error highlighting in stack traces (require 'compile) (setq valeri--repl-buffer-p t) (make-local-variable 'compilation-error-regexp-alist) (setq compilation-error-regexp-alist (cons (list valeri-traceback-line-re 1 2) compilation-error-regexp-alist)) (compilation-shell-minor-mode 1) (setq-local comint-prompt-regexp valeri-prompt-regexp) ;; Don't send initialization code until seeing the prompt to ensure that ;; the interpreter is ready. ;; (while (not (valeri-prompt-line)) ;; (accept-process-output (get-buffer-process (current-buffer))) ;; (goto-char (point-max))) ;; (valeri-send-string valeri-process-init-code) )) ;; when called interactively, switch to process buffer (if (called-interactively-p 'any) (switch-to-buffer valeri-process-buffer))) (defun valeri-prompt-line () (save-excursion (save-match-data (forward-line 0) (if (looking-at comint-prompt-regexp) (match-end 0))))) (provide 'valeri-mode) ;;; valeri-mode.el ends here