diff --git a/.projectile b/.projectile new file mode 100644 index 0000000000..0dd321e26b --- /dev/null +++ b/.projectile @@ -0,0 +1 @@ ++/goal_src diff --git a/scripts/emacs/goal-config.el b/scripts/emacs/goal-config.el new file mode 100644 index 0000000000..d4782cba65 --- /dev/null +++ b/scripts/emacs/goal-config.el @@ -0,0 +1,194 @@ +;; 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) + ("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 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 '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) + + ;; 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)))))