valeri/emacs/valeri-mode.el

152 lines
4.9 KiB
EmacsLisp

;;; 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 "\\(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)
;;;###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)))))
(defun valeri-comint-end-of-output-p (output)
"Return non-nil if OUTPUT ends with input prompt."
(string-match
;; XXX: It seems on macOS an extra carriage return is attached
;; at the end of output, this handles that too.
(concat
"\r?\n?"
;; Remove initial caret from calculated regexp
(replace-regexp-in-string
(rx string-start ?^) ""
valeri-prompt-regexp)
(rx eos))
output))
(defun valeri-choose-buffer-name (name)
(generate-new-buffer-name (format "*%s*" name)))
(defun run-valeri-new (command &optional name)
"Create a new inferior Valeri process in a new or existing buffer.
COMMAND is the command to call. NAME will be used for the name of
the buffer, defaults to \"valeri\"."
(setq name (or name "valeri"))
(let ((commandlist (split-string-and-unquote command))
(buffer (current-buffer))
(process-environment (copy-sequence process-environment)))
(set-buffer (apply 'make-comint-in-buffer
name
(valeri-choose-buffer-name name)
(car commandlist)
nil (cdr commandlist)))
)
(pop-to-buffer (current-buffer)))
(defun run-valeri-or-pop-to-buffer (command &optional name buffer)
(if (not (and buffer
(comint-check-proc buffer)))
(run-valeri-new command name)
(pop-to-buffer buffer)
))
(provide 'valeri-mode)
;;; valeri-mode.el ends here