FG42/lib/extensions/serene/serene-simple-mode.el

199 lines
7.0 KiB
EmacsLisp
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; serene-init --- The entry point for serene extension
;;; Commentary:
;;
;; In order to connect to Serene's nRepl process, first you have to make
;; that it's running some where (consult Serene's README for learning more
;; about it). Then simply use `serene-nrepl-connect' to make a connection.
;; at this point you can evaluate Serene's expressions by using `serene-eval-expr-at-point'
;; function which evaluates the expression before point end prints out the
;; result in minibuffer.
;;
;;; Code:
(defvar serene-simple-mode-map
(make-sparse-keymap))
(defvar serene-nrepl-host "127.0.0.1")
(defvar serene-nrepl-port 5544)
(defvar serene-nrepl-process nil)
(defconst serene-simple-mode-syntax-table
(let ((table (make-syntax-table)))
(modify-syntax-entry ?\" "\"" table)
;; / is punctuation, but // is a comment starter
(modify-syntax-entry ?\; "<" table)
(modify-syntax-entry ?\n ">" table)
table))
(defface serene-simple-mode-special-froms-face
'((t :inherit font-lock-builtin-face))
"Face of special forms."
:group 'simple-serene-mode)
(defface serene-simple-mode-builtin-fns-face
'((t :inherit font-lock-keyword-face))
"Face of builtin functions."
:group 'simple-serene-mode)
(defface serene-simple-mode-builtin-types-face
'((t :inherit font-lock-type-face))
"Face of built in types."
:group 'simple-serene-mode)
(defvar serene-simple-mode-special-forms
'("do" "let" "def" "fn" "quote" "cond" "if"
"defn" "defmacro" "list" "ns"))
(defconst serene-simple-mode-builtin-fns
'("=" ">" "<" ">=" "<=" "and" "or" "not" "first" "rest" "println"
"quit" "+" "*" "/" "-" "conj" "mod" "new" "pr" "prn" "print"))
(defconst serene-simple-mode-builtin-types
'("System" "String" "Boolean"))
(define-derived-mode serene-simple-mode
scheme-mode "Serene(Simple)"
"Major mode for Serene simple."
(define-key serene-simple-mode-map (kbd "C-x C-e") 'serene-eval-expr-at-point))
(defun serene-simple-add-keywords (face-name keyword-rules)
"Set the FACE-NAME for keywords in serene-simple using KEYWORD-RULES."
(let* ((keyword-list (mapcar #'(lambda (x)
(symbol-name (cdr x)))
keyword-rules))
(keyword-regexp
(concat
"\\("
(regexp-opt keyword-list)
"\\)")))
(font-lock-add-keywords 'serene-simple-mode
`((,keyword-regexp 1 ',face-name))))
(mapc #'(lambda (x)
(put (cdr x)
'serene-simple-indent-function
(car x)))
keyword-rules))
(serene-simple-add-keywords 'serene-simple-mode-special-froms-face
(mapcar (lambda (x) (cons 1 (intern x))) serene-simple-mode-special-forms))
(serene-simple-add-keywords 'serene-simple-mode-builtin-fns-face
(mapcar (lambda (x) (cons 1 (intern x))) serene-simple-mode-builtin-fns))
(serene-simple-add-keywords 'serene-simple-mode-builtin-types-face
(mapcar (lambda (x) (cons 1 (intern x))) serene-simple-mode-builtin-types))
(defun serene-expr-at-point ()
"Return sexp before the point."
(interactive)
(let ((opoint (point))
(left-quote ?)
expr)
(save-excursion
(with-syntax-table serene-simple-mode-syntax-table
;; If this sexp appears to be enclosed in `...' or ...
;; then ignore the surrounding quotes.
(cond ((eq (preceding-char) ?)
(progn (forward-char -1) (setq opoint (point))))
((or (eq (following-char) ?\')
(eq (preceding-char) ?\'))
(setq left-quote ?\`)))
;; When after a named character literal, skip over the entire
;; literal, not only its last word.
(when (= (preceding-char) ?})
(let ((begin (save-excursion
(backward-char)
(skip-syntax-backward "w-")
(backward-char 3)
(when (looking-at-p "\\\\N{") (point)))))
(when begin (goto-char begin))))
(forward-sexp -1)
;; If we were after `?\e' (or similar case),
;; use the whole thing, not just the `e'.
(when (eq (preceding-char) ?\\)
(forward-char -1)
(when (eq (preceding-char) ??)
(forward-char -1)))
;; Skip over hash table read syntax.
(and (> (point) (1+ (point-min)))
(looking-back "#s" (- (point) 2))
(forward-char -2))
(save-restriction
(if (eq (following-char) left-quote)
(forward-char))
(when (looking-at ",@?") (goto-char (match-end 0)))
(narrow-to-region (point-min) opoint)
(setq expr (read (current-buffer)))
expr)))))
(defun serene-eval-expr-at-point ()
"Send the expression at point to the nRepl for evaluation."
(interactive)
(if (and serene-nrepl-process (process-live-p serene-nrepl-process))
(let* ((buf (process-buffer serene-nrepl-process))
(expr (format "%s\n" (replace-regexp-in-string
(regexp-quote "\n")
" "
(prin1-to-string (serene-expr-at-point))
nil 'literal))))
(process-send-string serene-nrepl-process expr))
(message "Error: You need to connect to Serene's nRepl first.")))
(defun serene-nrepl-sentinel (process event)
"The sentinel fn for the given PROCESS to process the EVENT."
(message (format "Process: %s had the event '%s'" process event)))
(defun serene-process-incoming-result (process value)
"Process the incoming VALUE of the given PROCESS."
(if (not (= (length value) 0))
(let ((status (substring value 0 1)))
(if (string= status "0")
(message (format "=> %s" (substring value 1 -1)))
(message (format "Error: %s" (substring value 1 -1)))))
(message "Bad response from nRepl.")))
(defun serene-nrepl-connect ()
"Connect to the running nRepl of Serene and return the process value."
(interactive)
(if (not serene-nrepl-process)
(let ((p (open-network-stream "serene-nrepl"
(get-buffer-create "*serene-repl*")
serene-nrepl-host
serene-nrepl-port)))
(setq serene-nrepl-process p)
(set-process-sentinel p 'serene-nrepl-sentinel)
(set-process-filter p 'serene-process-incoming-result)
(message (format "Connected to tcp://%s:%s"
serene-nrepl-host
serene-nrepl-port)))
serene-nrepl-process))
(defun serene-nrepl-disconnect ()
"Disconnect from Serene nRepl."
(interactive)
(when serene-nrepl-process
(delete-process serene-nrepl-process)
(setq serene-nrepl-process nil)
(message "Disconnected from the nRepl.")))
(defun serene-nrepl-status ()
"Return the status of the connection to Serene's nRepl."
(interactive)
(if serene-nrepl-process
(message (format "%s" (process-status serene-nrepl-process)))
(message "Disconnected.")))
(provide 'extensions/serene/serene-simple-mode)
;;; serene-simple-mode.el ends here