2024-10-08 23:54:56 +00:00
|
|
|
;;; 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)
|
|
|
|
|
2024-10-12 17:29:49 +00:00
|
|
|
(defcustom valeri-prompt-regexp "\\(valeri>[\t ]+\\)+"
|
2024-10-08 23:54:56 +00:00
|
|
|
"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)
|
|
|
|
|
2024-10-12 17:29:49 +00:00
|
|
|
|
2024-10-08 23:54:56 +00:00
|
|
|
(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)))))
|
|
|
|
|
2024-10-12 17:29:49 +00:00
|
|
|
(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))
|
|
|
|
|
2024-10-12 18:13:42 +00:00
|
|
|
(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)
|
|
|
|
))
|
2024-10-08 23:54:56 +00:00
|
|
|
|
|
|
|
(provide 'valeri-mode)
|
|
|
|
|
|
|
|
;;; valeri-mode.el ends here
|