mirror of
https://github.com/open-goal/jak-project.git
synced 2025-02-17 04:27:57 +00:00
199 lines
8.2 KiB
EmacsLisp
199 lines
8.2 KiB
EmacsLisp
;; Emacs configuration for interacting with OpenGOAL files and compiler.
|
|
;; This sets up:
|
|
;; - some reasonable defaults for highlighting parens
|
|
;; - syntax highlighting
|
|
;; - indentation
|
|
;; - REPL integration
|
|
;; - shortcuts to compile files/s-expressions in the editor
|
|
|
|
;; To use this config, you can add something like this to your emacs config:
|
|
;; the path to the compiler:
|
|
;; (defconst opengoal-compiler-path "~/jak-project/build/goalc/goalc")
|
|
;; the path to this file (or you can copy this file to your emacs config)
|
|
;; (add-to-list 'load-path "~/jak-project/scripts/emacs")
|
|
;; load this file!
|
|
;; (load "goal-config")
|
|
|
|
;; Other recommended tools:
|
|
;; - projectile: search through all .gc files
|
|
;; - doom emacs: make emacs look fancy and have good defaults. It is very slow with default settings.
|
|
|
|
|
|
|
|
(defun lispy-parens ()
|
|
"Set up parens in a reasonable way."
|
|
;; based on https://www.emacswiki.org/emacs/ShowParenMode
|
|
;; remove the stupid delay when highlighting parens. Emacs is slow enough without intentional delays
|
|
(setq show-paren-delay 0)
|
|
(setq show-paren-style 'parenthesis)
|
|
(make-variable-buffer-local 'show-paren-mode)
|
|
(show-paren-mode 1)
|
|
;; the default color is hard to see - make it the most painful color possible.
|
|
(custom-set-faces
|
|
'(show-paren-match ((t (:foreground "white" :background "red")))))
|
|
)
|
|
|
|
;; very simple syntax highlighting (sometimes fails)
|
|
;; these are added to lisp-mode's highlighting.
|
|
(font-lock-add-keywords 'lisp-mode
|
|
'(("set!" . font-lock-keyword-face)
|
|
("local-vars" . font-lock-keyword-face)
|
|
("until" . font-lock-keyword-face)
|
|
("car" . font-lock-keyword-face)
|
|
("cdr" . font-lock-keyword-face)
|
|
("->" . font-lock-keyword-face)
|
|
("else" . font-lock-constant-face)
|
|
("nop!" . font-lock-builtin-face)
|
|
;;("and" . font-lock-keyword-face)
|
|
;;("or" . font-lock-keyword-face)
|
|
("format" . font-lock-builtin-face)
|
|
("defun-debug" . font-lock-keyword-face)
|
|
("defbehavior" . font-lock-keyword-face)
|
|
("defenum" . font-lock-keyword-face)
|
|
("begin" . font-lock-keyword-face)
|
|
("zero?" . font-lock-builtin-face)
|
|
("#f" . font-lock-constant-face)
|
|
("#t" . font-lock-constant-face)))
|
|
|
|
;; more advanced syntax highlighting. These still fail sometimes, but look nicer.
|
|
(defconst scheme-font-lock-keywords-1
|
|
(eval-when-compile
|
|
(list
|
|
;;
|
|
;; Declarations. Hannes Haug <hannes.haug@student.uni-tuebingen.de> says
|
|
;; this works for SOS, STklos, SCOOPS, Meroon and Tiny CLOS.
|
|
(list (concat "(\\(define\\*?\\("
|
|
;; Function names.
|
|
"\\(\\|-public\\|-method\\|-generic\\(-procedure\\)?\\)\\|"
|
|
;; Macro names, as variable names. A bit dubious, this.
|
|
"\\(-syntax\\|-macro\\)\\|"
|
|
;; Class names.
|
|
"-class"
|
|
;; Guile modules.
|
|
"\\|-module"
|
|
"\\)\\)\\>"
|
|
;; Any whitespace and declared object.
|
|
;; The "(*" is for curried definitions, e.g.,
|
|
;; (define ((sum a) b) (+ a b))
|
|
"[ \t]*(*"
|
|
"\\(\\sw+\\)?")
|
|
'(1 font-lock-keyword-face)
|
|
'(6 (cond ((match-beginning 3) font-lock-function-name-face)
|
|
((match-beginning 5) font-lock-variable-name-face)
|
|
(t font-lock-type-face))
|
|
nil t))
|
|
))
|
|
"Subdued expressions to highlight in Scheme modes.")
|
|
(font-lock-add-keywords 'lisp-mode scheme-font-lock-keywords-1)
|
|
|
|
;; make gc files use lisp-mode
|
|
(add-to-list 'auto-mode-alist '("\\.gc\\'" . lisp-mode))
|
|
;; run setup-goal when we enter lisp mode.
|
|
(add-hook 'lisp-mode-hook 'setup-goal)
|
|
|
|
(defun setup-goal ()
|
|
"Check if we are in a GOAL file. If so, change settings for GOAL."
|
|
(when (and (stringp buffer-file-name)
|
|
(string-match "\\.gc\\'" buffer-file-name))
|
|
|
|
;; set up indentation for GOAL keywords
|
|
(put 'with-pp 'common-lisp-indent-function 0)
|
|
(put 'while 'common-lisp-indent-function 1)
|
|
(put 'goal-src 'common-lisp-indent-function 1)
|
|
(put 'engine-src 'common-lisp-indent-function 1)
|
|
(put 'rlet 'common-lisp-indent-function 1)
|
|
(put 'until 'common-lisp-indent-function 1)
|
|
(put 'countdown 'common-lisp-indent-function 1)
|
|
(put 'defun-debug 'common-lisp-indent-function 2)
|
|
(put 'defenum 'common-lisp-indent-function 2)
|
|
(put 'defbehavior 'common-lisp-indent-function 3)
|
|
|
|
;; indent for common lisp, this makes if's look nicer
|
|
(custom-set-variables '(lisp-indent-function 'common-lisp-indent-function))
|
|
(autoload 'common-lisp-indent-function "cl-indent" "Common Lisp indent.")
|
|
;; use spaces, not tabs
|
|
(setq-default indent-tabs-mode nil)
|
|
;; set up parens
|
|
(lispy-parens)
|
|
;; set up keyboard shortcuts
|
|
(local-set-key (kbd "C-x C-t") 'opengoal-send-to-repl)
|
|
(local-set-key (kbd "C-x C-g") 'opengoal-ml-file)
|
|
)
|
|
)
|
|
|
|
;; this will make some but not all colors work.
|
|
;; I think only very simple "terminal color" settings will work in emacs, but
|
|
;; OpenGOAL uses fancy xterm color codes in places.
|
|
(add-to-list 'comint-output-filter-functions 'ansi-color-process-output)
|
|
|
|
;; OpenGOAL compiler interaction
|
|
(defun run-opengoal ()
|
|
"Switch to the OpenGOAL compiler. Will open a new one if there is none."
|
|
;; check if we have it running already
|
|
(interactive)
|
|
(let ((buffer (comint-check-proc "OpenGOAL")))
|
|
;; create or reuse a buffer and make it visible
|
|
(pop-to-buffer
|
|
(if (or buffer (not (derived-mode-p 'opengoal-mode))
|
|
(comint-check-proc (current-buffer)))
|
|
(get-buffer-create (or buffer "*OpenGOAL*"))
|
|
(current-buffer)))
|
|
|
|
(unless buffer
|
|
;; start the compiler
|
|
(ansi-color-for-comint-mode-on)
|
|
(apply 'make-comint-in-buffer "OpenGOAL" buffer
|
|
opengoal-compiler-path '()))))
|
|
|
|
(defun opengoal-local-path (filename)
|
|
"Convert a file path to one that works in the compiler."
|
|
;; this is a bit of a hack.
|
|
(string-match "jak-project/*" filename) ;; windows?
|
|
(substring filename (+ (match-beginning 0) 12)))
|
|
|
|
(defun opengoal-compile-file (filename mode)
|
|
"Send a command to the compiler to compile a file."
|
|
;; make a command like (m "my-file.gc")
|
|
(let ((cmd (format "(%s \"%s\")\n" mode (opengoal-local-path filename))))
|
|
;; switch to OpenGOAL buffer
|
|
(run-opengoal)
|
|
;; send it!
|
|
(comint-send-string "*OpenGOAL*" cmd)))
|
|
|
|
(defun opengoal-compile-current-file (mode)
|
|
"Check if the current file is saved. If so, compile it!"
|
|
(cond
|
|
((buffer-modified-p)
|
|
(message "Cannot compile current file: save your changes first!"))
|
|
(t
|
|
(opengoal-compile-file (buffer-file-name) mode))))
|
|
|
|
(defun opengoal-m-file ()
|
|
"Make the current file."
|
|
(interactive)
|
|
(opengoal-compile-current-file "m"))
|
|
|
|
(defun opengoal-md-file ()
|
|
"Make and current file and print x86 disassembly."
|
|
(interactive)
|
|
(opengoal-compile-current-file "md"))
|
|
|
|
(defun opengoal-ml-file ()
|
|
"Make and load the current file."
|
|
(interactive)
|
|
(opengoal-compile-current-file "ml"))
|
|
|
|
(defun opengoal-send-to-repl ()
|
|
(interactive)
|
|
;; this is a bit of a hack to work around newline stuff.
|
|
;; This grabs the text, removes line comments and newlines, and sends it to the REPL.
|
|
(save-excursion
|
|
(beginning-of-defun)
|
|
(let ((start-pos (point)))
|
|
(end-of-sexp)
|
|
(let* ((end-pos (point))
|
|
(text (buffer-substring start-pos end-pos))
|
|
(text-no-comment (replace-regexp-in-string ";;.*$" " " text nil 'literal))
|
|
(flat-string (concat (replace-regexp-in-string "\n" " " text-no-comment nil 'literal) "\n")))
|
|
(comint-send-string "*OpenGOAL*" flat-string)))))
|