| git.druid.rocks | index | druid520 | radium | radium-lang-kona.el |
radium-lang-kona.el
;;; radium-lang-kona.el, minimal major mode + repl for kona (k) -*- lexical-binding: t; -*-
;;
;; no package on elpa/melpa covers k, so this is written from scratch:
;; indentation off prog-mode defaults, comint repl, fontification.
(require 'comint)
(define-derived-mode kona-mode prog-mode "k"
"my major mode for kona (.k) source: indentation, repl and fontification."
(setq-local comment-start "/ ")
(setq-local comment-end "")
(setq-local indent-tabs-mode t)
(setq-local tab-width 8)
(setq-local imenu-generic-expression
'((nil "^\\([A-Za-z.][A-Za-z0-9._]*\\)[ \t]*:[ \t]*{" 1)))
(setq-local font-lock-defaults
'(radium-kona-font-lock-keywords nil nil nil nil
(font-lock-syntactic-face-function . radium-kona-syntax-face))))
(defvar radium-kona-font-lock-keywords
'(("\\b\\(if\\|while\\|do\\|return\\|=:\\)\\b" 1 font-lock-keyword-face)
("\\(:[0-9]+\\)" 1 font-lock-constant-face)
("\\(0[xX][0-9a-fA-F]+\\|[0-9]+\\.[0-9]*[eE]?[+-]?[0-9]*\\)" 1 font-lock-constant-face))
"a tiny k keyword/constant table so .k files aren't left plain.")
(defun radium-kona-syntax-face (state)
(if (nth 3 state) font-lock-string-face font-lock-comment-face))
(add-to-list 'auto-mode-alist '("\\.k\\'" . kona-mode))
(add-to-list 'auto-mode-alist '("\\.ks\\'" . kona-mode))
;; a kona one-liner script with no .k/.ks extension (`#!/usr/bin/env
;; k`) still needs its mode picked from the shebang - emacs has no
;; built-in interpreter-mode-alist entry for "k" at all (it isn't a
;; language emacs ships support for), unlike perl/sh/scheme below.
(add-to-list 'interpreter-mode-alist '("k" . kona-mode))
(defvar radium-kona-program "k"
"the kona interpreter binary - kona is the language, the executable
is just `k'.")
(defun radium-kona-trim-output (string)
"strip kona's double-space indent from repl output lines - the same
thing ks.sh's sed does for script runs. comint-preoutput-filter-
functions can hand this one pty chunk at a time, not one logical
line at a time - a chunk boundary landing exactly between a newline
and kona's own 2-space indent leaves that one line's indent
untrimmed (cosmetic only, and narrow enough - pty buffering, not
buffer content - that it wasn't worth chasing further here)."
(replace-regexp-in-string "\\(\\`\\|\n\\) " "\\1" string))
(defun radium-run-kona ()
(interactive)
(let ((buf (get-buffer-create "*kona*")))
(unless (comint-check-proc buf)
(with-current-buffer buf
(make-comint-in-buffer "kona" buf radium-kona-program nil)
(comint-mode)
(add-hook 'comint-preoutput-filter-functions #'radium-kona-trim-output nil t)))
(pop-to-buffer buf)))
(defun radium-kona-send-region (start end)
"i send START..END to the kona repl, spawning it first if it isnt up.
with no active region i send the current line instead, so a kona
one-liner dropped into any file - a c program, a comment, anything -
just works. the text is captured before i pop to the repl, so
START/END always mean the source buffer."
(interactive
(if (use-region-p)
(list (region-beginning) (region-end))
(list (line-beginning-position) (line-end-position))))
(let ((text (buffer-substring-no-properties start end)))
(radium-run-kona)
(comint-send-string (get-buffer-process "*kona*") (concat text "\n"))))
(defun radium-kona-send-line ()
"i send the current line to the kona repl, wherever i am."
(interactive)
(radium-kona-send-region (line-beginning-position) (line-end-position)))
;; global on purpose: a kona expression belongs anywhere - drop one
;; into a c buffer, C-c C-l, get the answer. major-mode bindings still
;; win in their own buffers (eval-defun in elisp, interrupt in comint,
;; geiser's scheme keys, etc), so these only fire where nothing local
;; claims the key.
(global-set-key (kbd "C-c C-c") #'radium-kona-send-region)
(global-set-key (kbd "C-c C-l") #'radium-kona-send-line)
(global-set-key (kbd "C-c C-z") #'radium-run-kona)
;; modes that claim these keys for business i don't want give them
;; back, so kona works in the buffers i actually write code in:
;; - cc-mode: comment-region / the electric toggle / defun-name display
;; (M-; comment-dwim still comments regions)
;; - sh-mode: its skeleton inserters (sh-case/sh-indexed-loop/
;; sh-show-shell) - still M-x-able, and the sh snippets cover me
;; - ada-mode: C-c C-c is ada-build-make, but radium's C-c b b already
;; builds ada projects, so kona takes the key
;; - forth-mode: load-file/switch-to-output, both dead without a forth
;; repl - which i deliberately don't wire (forthc is one-shot)
;; geiser keeps its own scheme keys: scheme already has the right repl
;; and eval keys there, no kona needed.
(with-eval-after-load 'cc-mode
(define-key c-mode-map (kbd "C-c C-c") #'radium-kona-send-region)
(define-key c-mode-map (kbd "C-c C-l") #'radium-kona-send-line)
(define-key c-mode-map (kbd "C-c C-z") #'radium-run-kona))
(with-eval-after-load 'sh-script
(define-key sh-mode-map (kbd "C-c C-c") #'radium-kona-send-region)
(define-key sh-mode-map (kbd "C-c C-l") #'radium-kona-send-line)
(define-key sh-mode-map (kbd "C-c C-z") #'radium-run-kona))
(with-eval-after-load 'ada-mode
(define-key ada-mode-map (kbd "C-c C-c") #'radium-kona-send-region))
(with-eval-after-load 'forth-mode
(define-key forth-mode-map (kbd "C-c C-l") #'radium-kona-send-line)
(define-key forth-mode-map (kbd "C-c C-z") #'radium-run-kona))
(provide 'radium-lang-kona)