aboutsummaryrefslogtreecommitdiff
path: root/emacs/skribilo.el
diff options
context:
space:
mode:
authorArun Isaac2021-07-06 23:46:06 +0530
committerLudovic Courtès2021-09-29 18:31:19 +0200
commit1f2b188e100cb6261d686a51692968591043f218 (patch)
tree32a4a2aa1bc0c09d501bed008f068c43f3b77d35 /emacs/skribilo.el
parent671e55bb3bbbb3467dee5d6e438ea897acda814e (diff)
downloadskribilo-1f2b188e100cb6261d686a51692968591043f218.tar.gz
skribilo-1f2b188e100cb6261d686a51692968591043f218.tar.lz
skribilo-1f2b188e100cb6261d686a51692968591043f218.zip
emacs: Add skribilo minor mode.
* emacs/skribilo.el: New file. * emacs/Makefile.am (lisp_DATA): Add it. Signed-off-by: Ludovic Courtès <ludo@gnu.org>
Diffstat (limited to 'emacs/skribilo.el')
-rw-r--r--emacs/skribilo.el114
1 files changed, 114 insertions, 0 deletions
diff --git a/emacs/skribilo.el b/emacs/skribilo.el
new file mode 100644
index 0000000..9f9bc52
--- /dev/null
+++ b/emacs/skribilo.el
@@ -0,0 +1,114 @@
+;;; skribilo.scm -- The Skribilo document processor.
+;;;
+;;; Copyright © 2021 Arun Isaac <arunisaac@systemreboot.net>
+;;;
+;;; This file is part of Skribilo.
+;;;
+;;; Skribilo is free software: you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation, either version 3 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; Skribilo is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with Skribilo. If not, see <http://www.gnu.org/licenses/>.
+
+(require 'scheme)
+
+(defconst skribilo-font-lock-keywords
+ (list (cons (rx "(" (group (or "document" "chapter" "code"
+ "section" "subsection" "subsubsection"
+ "prog" "p" "ref")))
+ 1)))
+
+(defun skribilo-innermost-parens-start (state)
+ "Return the position of the innermost open parenthesis. STATE
+is a parser state object as returned by `parse-partial-sexp'."
+ (elt state 1))
+
+(defun skribilo-open-parens-positions (state)
+ "Return the positions of the currently open parentheses,
+starting with the outermost. STATE is a parser state object as
+returned by `parse-partial-sexp'. See \"(elisp) Parser state\"."
+ (elt state 9))
+
+(defun skribilo-sexp-head (state)
+ "Return the first symbol of the innermost sexp. STATE is a
+parser state object as returned by `parse-partial-sexp'."
+ (save-excursion
+ (goto-char (skribilo-innermost-parens-start state))
+ (forward-char)
+ (intern-soft
+ (buffer-substring-no-properties
+ (point)
+ (progn (forward-sexp) (point))))))
+
+(defun skribilo-indent-function (indent-point state)
+ (let ((property (get (skribilo-sexp-head state)
+ 'skribilo-indent-function)))
+ (cond
+ ;; If within square brackets, don't indent.
+ ((seq-some (lambda (position)
+ (eq (char-after position)
+ ?\[))
+ (skribilo-open-parens-positions state))
+ 0)
+ ;; Like `scheme-indent-function', but use the
+ ;; 'skribilo-indent-function property instead of the
+ ;; 'scheme-indent-function property.
+ ((eq property 'defun)
+ (lisp-indent-defform state indent-point))
+ ((integerp property)
+ (lisp-indent-specform method state indent-point (current-column)))
+ ((functionp property)
+ (funcall property state indent-point (current-column)))
+ ;; Else, pass on control to `scheme-indent-function'.
+ (t
+ (scheme-indent-function indent-point state)))))
+
+(defun skribilo-keywords-in-sexp ()
+ "Return the number of keywords in the sexp currently at
+point. The point must be on the opening parenthesis at the
+beginning of the sexp."
+ ;; The [] construct in skribilo isn't a proper sexp in the sense
+ ;; understood by `read'. So, we can't simply use `sexp-at-point' to
+ ;; read the whole sexp and then count the keywords.
+ (save-excursion
+ ;; TODO: What if there is whitespace after the opening
+ ;; parenthesis?
+ ;; Move into the sexp.
+ (forward-char)
+ ;; Go to the end of the first sub-sexp.
+ (forward-sexp)
+ ;; Move past one sub-sexp and increment result if that sub-sexp is
+ ;; a keyword.
+ (let ((result 0))
+ (while (ignore-error 'scan-error
+ (or (forward-sexp) t))
+ (when (keywordp (sexp-at-point))
+ (setq result (1+ result))))
+ result)))
+
+(defun skribilo-indent-form (state indent-point normal-indent)
+ (lisp-indent-specform
+ (* 2
+ (save-excursion
+ (goto-char (skribilo-innermost-parens-start state))
+ (skribilo-keywords-in-sexp)))
+ state indent-point normal-indent))
+
+(define-minor-mode skribilo-mode
+ "Minor mode for editing Skribilo sources."
+ nil nil nil
+ (setq-local lisp-indent-function 'skribilo-indent-function)
+ (mapc (lambda (symbol)
+ (put symbol 'skribilo-indent-function 'skribilo-indent-form))
+ '(document chapter section subsection subsubsection))
+ (font-lock-add-keywords nil skribilo-font-lock-keywords)
+ (font-lock-flush))
+
+(provide 'skribilo)