;;; sisu-spine-mode.el --- Major mode for SiSU (spine parser) markup text

;; Copyright (C) 2011, 2021  Free Software Foundation, Inc.

;; Author: Ralph Amissah & Ambrose Kofi Laing
;; Maintainer: Ralph Amissah <ralph.amissah@gmail.com>
;; Keywords: text, syntax, processes, tools
;; Version:   8.0.0
;; URL: https://www.sisudoc.org/
;; originally looked at (based on) doc-mode, with kind permission of the author
;;   Author: SUN, Tong <suntong001@users.sf.net>, (c)2001-6, all right reserved
;;   Version: $Date: 2006/01/19 03:13:41 $ $Revision: 1.14 $
;;   Home URL: https://xpt.sourceforge.net/
;; with contributions from Kevin Ryde and Stefan Monnier

;; This program 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, or (at your option)
;; any later version.
;;
;; This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.

;; Viva Software Libre!
;; Support the free software movement!
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; Commentary:

;; SiSU (https://www.sisudoc.org/) is a document structuring and
;; publishing framework.  This package provides an Emacs major mode
;; for SiSU markup, as used by the spine parser (in D) which has a different
;; header (based on yaml) from the original sisu parser (in Ruby) which has
;; bespoke headers.

;; When this package is installed, files ending in ".sst" are automatically
;; associated with sisu-spine-mode.  If a file doesn't have a
;; .sst extension, add a first line:
;; # -*- sisuSpine -*-

;; The documentation for the "Structure Of The Hierarchy Text" can be
;; found in the sisustring for the sisu-spine-mode function.

;;; Code:

;; Variables:

(defgroup sisu-faces nil
  "AsciiSisu highlighting"
  :group 'sisus)

;; == Colors
; color n is more prominent than color n+1

(defface sisu-title-1-face
  `((((class color)
      (background dark))
     (:foreground "brown3" :bold t :height 1.2 :inherit variable-pitch))
    (((class color)
      (background light))
     (:foreground "brown3" :bold t :height 1.2 :inherit variable-pitch))
    (t (:weight bold :inherit variable-pitch)))
  "Face for AsciiSisu titles at level 1."
  :group 'sisu-faces)

(defface sisu-title-2-face
  `((((class color)
      (background dark))
     (:foreground "yellow4" :bold t :height 1.1 :inherit variable-pitch))
    (((class color)
      (background light))
     (:foreground "yellow4" :bold t :height 1.1 :inherit variable-pitch))
    (t (:weight bold :inherit variable-pitch)))
  "Face for AsciiSisu titles at level 2."
  :group 'sisu-faces)

(defface sisu-title-3-face
  `((((class color)
      (background dark))
     (:foreground "sienna3" :bold t))
    (((class color)
      (background light))
     (:foreground "sienna3" :bold t))
    (t (:weight bold)))
  "Face for AsciiSisu titles at level 3."
  :group 'sisu-faces)

(defface sisu-title-4-face
  `((((class color)
      (background dark))
     (:foreground "burlywood3"))
    (((class color)
      (background light))
     (:foreground "burlywood3"))
    (t ()))
  "Face for AsciiSisu titles at level 4."
  :group 'sisu-faces)

(defface info-node
  '((((class color) (background light)) (:foreground "brown" :bold t :italic t))
    (((class color) (background dark)) (:foreground "white" :bold t :italic t))
    (t (:bold t :italic t)))
  "Face for Info node names."
  :group 'sisu-faces)

(defvar sisu-title-1 'sisu-title-1-face)
(defvar sisu-title-2 'sisu-title-2-face)
(defvar sisu-title-3 'sisu-title-3-face)
(defvar sisu-title-4 'sisu-title-4-face)

(defvar sisu-general-font-lock-red1 font-lock-warning-face)
(defvar sisu-general-font-lock-red2 font-lock-comment-face)
(defvar sisu-general-font-lock-red3 font-lock-string-face)

(defvar sisu-general-font-lock-green1 font-lock-type-face)
(defvar sisu-general-font-lock-green2 font-lock-constant-face)

(defvar sisu-general-font-lock-blue1 font-lock-keyword-face)
(defvar sisu-general-font-lock-blue2 font-lock-function-name-face)
(defvar sisu-general-font-lock-blue3 font-lock-builtin-face)

(defvar sisu-general-font-lock-yellow1 font-lock-variable-name-face)
(defvar sisu-general-font-lock-yellow2 font-lock-comment-face)

;; == sisu-spine-mode settings

(defvar sisu-spine-mode-hook nil
  "Normal hook run when entering Sisu Text mode.")

(defvar sisu-spine-mode-abbrev-table nil
  "Abbrev table in use in Sisu-spine-mode buffers.")
(define-abbrev-table 'sisu-spine-mode-abbrev-table ())

(defconst sisu-font-lock-keywords
  (eval-when-compile
    (list
      ;;grouped text ---------
      ;(cons "^```[ ]code\\(.\\|\n\\)+?\n```\n"      'sisu-general-font-lock-red2)
      (cons "^```[ ]+code.*?$\\|^```$"  'sisu-general-font-lock-red2)
      (cons "^```[ ]+table.*?$\\|^```$" 'sisu-general-font-lock-red2)
      (cons "^```[ ]+group$\\|^```$"    'sisu-general-font-lock-red2)
      (cons "^```[ ]+block$\\|^```$"    'sisu-general-font-lock-red2)
      (cons "^```[ ]+poem$\\|^```$"     'sisu-general-font-lock-red2)
      (cons "^```[ ]+alt$\\|^```$"      'sisu-general-font-lock-red2)
      ;;grouped text ---------
      (cons "^group{\\|^}group"       'sisu-general-font-lock-red2)
      (cons "^block{\\|^}block"       'sisu-general-font-lock-red2)
      (cons "^code{\\|^}code"         'sisu-general-font-lock-red2)
      (cons "^poem{\\|^}poem"         'sisu-general-font-lock-red2)
      (cons "^alt{\\|^}alt"           'sisu-general-font-lock-red2)
      (cons "^table{.+\\|^}table"     'sisu-general-font-lock-red2)
      (cons "^{table[^}]+}"           'sisu-general-font-lock-red2)

      (list
        (concat
          "^\`\\{3\\}[ ]+code.*?$"
          "\\(.\\|\n\\)+?"
          "\`\\{3\\}$"
        )
        '(1 sisu-general-font-lock-red2 t)
        '(2 nil t)
        '(3 sisu-general-font-lock-red2 t)
      )
      (list
        (concat
          "^\`\\{3\\}[ ]+table.*?$"
          "\\(.\\|\n\\)+?"
          "\`\\{3\\}$"
        )
        '(1 sisu-general-font-lock-red2 t)
        '(2 nil t)
        '(3 sisu-general-font-lock-red2 t)
      )
      (list
        (concat
          "^\`\\{3\\}[ ]+\\(group\\|block\\|alt\\|poem\\)$"
          "\\(.\\|\n\\)+?"
          "^\`\\{3\\}$"
        )
        '(1 sisu-general-font-lock-red2 t)
        '(2 nil t)
        '(3 sisu-general-font-lock-red2 t)
      )

      ;; footnote/endnote ----
      ;(cons "\~{.+?}\~"  'sisu-general-font-lock-green1)
      (cons "\~{\\*\\*\\|\~{\\*\\|\~{\\|}\~"   'sisu-general-font-lock-red2)
      (cons "\~\\[\\+\\|\~\\[\\*\\|\~\\[\\|\\]\~"  'sisu-general-font-lock-red2)
      (cons "\~\\^ \\|^\\^\~ " 'sisu-general-font-lock-red2)
      (list
        (concat
          "\\(\*\~\\)"
          "\\([^ \r\t\n]+\\)"
        )
        '(1 sisu-general-font-lock-red1 t)
        '(2 sisu-general-font-lock-blue2 t)
      )

      ;; emphasis (can be program configured to be bold italics or underscore)
      (list
        (concat
          "\\([*]{\\)"
          "\\([^}]+\\)"
          "\\(}[*]\\)"
        )
        '(1 sisu-general-font-lock-red1 t)
        '(2 sisu-general-font-lock-red1 t)
        '(3 sisu-general-font-lock-red1 t)
      )

      ;; bold ----------------
      (list
        (concat
          "\\([!]{\\)"
          "\\([^}]+\\)"
          "\\(}[!]\\)"
        )
        '(1 sisu-general-font-lock-red1 t)
        '(2 sisu-general-font-lock-red1 t)
        '(3 sisu-general-font-lock-red1 t)
      )
      (cons "\\*[^ ]+\\*"               'sisu-general-font-lock-red1)
      (cons "^!_ .+"                    'sisu-general-font-lock-red1)

      ;; italics -------------
      (list
        (concat
          "\\([/]{\\)"
          "\\([^}]+\\)"
          "\\(}[/]\\)"
        )
        '(1 sisu-general-font-lock-red1 t)
        '(2 sisu-general-font-lock-blue1 t)
        '(3 sisu-general-font-lock-red1 t)
      )

      ;; underscore ----------
      (list
        (concat
          "\\([_]{\\)"
          "\\([^}]+\\)"
          "\\(\}[_]\\)"
        )
        '(1 sisu-general-font-lock-red1 t)
        '(2 sisu-general-font-lock-red1 t)
        '(3 sisu-general-font-lock-red1 t)
      )

      ;; monospace -----------
      (list
        (concat
          "\\([#]{\\)"
          "\\([^}]+\\)"
          "\\(}[#]\\)"
        )
        '(1 sisu-general-font-lock-red1 t)
        '(2 sisu-general-font-lock-red1 t)
        '(3 sisu-general-font-lock-red1 t)
      )

      ;; citation ------------
      (list
        (concat
          "\\([\"]{\\)"
          "\\([^}]+\\)"
          "\\(}[\"]\\)"
        )
        '(1 sisu-general-font-lock-red1 t)
        '(2 sisu-general-font-lock-red1 t)
        '(3 sisu-general-font-lock-red1 t)
      )

      ;; inserted text -------
      (list
        (concat
          "\\([\+]{\\)"
          "\\([^}]+\\)"
          "\\(}[\+]\\)"
        )
        '(1 sisu-general-font-lock-red1 t)
        '(2 sisu-general-font-lock-red1 t)
        '(3 sisu-general-font-lock-red1 t)
      )

      ;; strike through ------
      (list
        (concat
          "\\(\\-{\\)"
          "\\([^}]+\\)"
          "\\(}\\-\\)"
        )
        '(1 sisu-general-font-lock-red1 t)
        '(2 sisu-general-font-lock-red1 t)
        '(3 sisu-general-font-lock-red1 t)
      )

      ;; superscript ---------
      (list
        (concat
          "\\(\\^{\\)"
          "\\([^}]+\\)"
          "\\(}\\^\\)"
        )
        '(1 sisu-general-font-lock-red1 t)
        '(2 sisu-general-font-lock-red1 t)
        '(3 sisu-general-font-lock-red1 t)
      )

      ;; subscript -----------
      (list
        (concat
          "\\([,]{\\)"
          "\\([^}]+\\)"
          "\\(}[,]\\)"
        )
        '(1 sisu-general-font-lock-red1 t)
        '(2 sisu-general-font-lock-red1 t)
        '(3 sisu-general-font-lock-red1 t)
      )

      ;; numbered list
      (cons "^# \\|^_# "                'sisu-general-font-lock-red1)

      ;; bullet text
      (cons "^_\\*[1-9] \\|^_\\* "      'sisu-general-font-lock-red1)

      ;; indented text
      (cons "^_[1-9] "                  'sisu-general-font-lock-red1)
      (cons "^_[1-9]! "                 'sisu-general-font-lock-red1)

      ;; hanging indented text [proposed enable when implemented]
      (cons "^__[1-9] "                'sisu-general-font-lock-red1)
      (cons "^_[0-9]_[0-9] "           'sisu-general-font-lock-red1)
      (cons "^__[1-9]! "               'sisu-general-font-lock-red1)
      (cons "^_[0-9]_[0-9]! "          'sisu-general-font-lock-red1)

      ;; url
      (cons "\\(^\\|[ ]\\)https?:[/][/][^ \t\n\r<]+" 'sisu-general-font-lock-blue2)

      ;; Comment Lines
      (cons "^% .*"                     'sisu-general-font-lock-blue1)

      ;; page break
      (cons "^\\(-\\\\\\\\-\\|=\\\\\\\\=\\|-\\.\\.-\\)" 'sisu-general-font-lock-red2)

      ;; line break
      (cons " \\\\\\\\ "                'sisu-general-font-lock-red1)

      ;; line break (depreciated)
      (cons "<br>"                      'sisu-general-font-lock-red1)

      ;; Section titles
      (list "^\\(\\([1-4]\\|:?[A-D]\\)\\~\\)\\(.*\\)"
        '(1 sisu-title-1 t)
        '(3 sisu-title-2 t)
      )

      ;; hyper-links
      (list
        (concat
          "\\({~^\\|{\\)"
          "\\([^}{]+\\)"
          "\\(}https?:[/][/][^ \r\n\t<]+\\)"
        )
        '(1 sisu-general-font-lock-blue2 t)
        '(2 sisu-general-font-lock-red1 t)
        '(3 sisu-general-font-lock-blue2 t)
      )

      ;; book index
      (list
        (concat
          "^\\(\={\\)"
          "\\([^}{]+\\)"
          "\\(}\\)$"
        )
        '(1 sisu-general-font-lock-green1 t)
        '(2 nil t)
        '(3 sisu-general-font-lock-green1 t)
      )

      ;(cons "^\={.+}"                 'sisu-general-font-lock-green1)

      ;; numbers
      (cons "\\<[.0-9]+\\>"             'sisu-general-font-lock-green2)

      ;; bullets sisu_normal (nearly copied regexp)
      (cons "^_\\([1-9*]\\|[1-9]\\*\\) " 'sisu-general-font-lock-blue2)

      ;; image links
      (list
        (concat
          "\\({\\)"
          "\\([^}{]+\\)"
          "\\(}image\\)"
        )
        '(1 sisu-general-font-lock-blue2 t)
        '(2 sisu-general-font-lock-red1 t)
        '(3 sisu-general-font-lock-blue2 t)
      )

      ;; insert file links
      (list
        (concat
          "\\(<< \\)"
          "\\([^ \r\t\n]+\\.ss\\)"
          "\\(i\\|t\\)"
        )
        '(1 sisu-general-font-lock-blue2 t)
        '(2 sisu-general-font-lock-blue2 t)
        '(3 sisu-general-font-lock-blue2 t)
      )

      ;; raw keywords
      (list
        (concat
          "^\\(\\("
          "creator\\|"
          "title\\|"
          "date\\|"
          "rights\\|"
          "publisher\\|"
          "classify\\|"
          "identifier\\|"
          "original\\|"
          "notes\\|"
          "links\\|"
          "make\\|"
          "\\):\\)\\(.*\\)"
        )
        '(1 sisu-title-2 keep)
        '(3 sisu-title-3 keep)
      )
    )
  )
  "Default expressions to highlight in AsciiSisu mode."
)

;; outline mode evil "folding" if available
;; (define-key evil-normal-state-map ",0"   'show-all)
;; (define-key evil-normal-state-map ",-"   'hide-body)
;; (define-key evil-normal-state-map ",+"   'show-subtree)
;; (define-key evil-normal-state-map ",="   'show-subtree)

;;

;; Sisu & Autoload:

;;;###autoload
(define-derived-mode sisu-spine-mode text-mode "SiSU"
  "Major mode for editing SiSU files.
SiSU document structuring, publishing in multiple formats and search.
URL `https://www.sisudoc.org/'"
  (modify-syntax-entry ?\'  ".")
  ;;(flyspell-mode nil)

  (make-local-variable 'paragraph-start)
  (setq paragraph-start (concat "$\\|>" page-delimiter))
  (make-local-variable 'paragraph-separate)
  (setq paragraph-separate paragraph-start)
  (make-local-variable 'paragraph-ignore-fill-prefix)
  (setq paragraph-ignore-fill-prefix t)

  (set (make-local-variable 'outline-regexp)
       "^\\(\\([1-4]\\|:?[A-D]\\)\\~\\|\\@[a-z]+:\\( \\|$\\)\\)")

  (make-local-variable 'require-final-newline)
  (setq require-final-newline t)

  (make-local-variable 'font-lock-defaults)
  (setq font-lock-defaults
        '(sisu-font-lock-keywords
          nil                           ; KEYWORDS-ONLY: no
          nil                           ; CASE-FOLD: no
          ((?_ . "w"))                  ; SYNTAX-ALIST
          ))
  ;; Enable outlining.
  ;; TODO with outlining make sure linum (line numbering) is off,
  ;; else performance penalty, sucks bigtime
  (outline-minor-mode 1))

;;;###autoload (add-to-list 'auto-mode-alist '("\\.ss[imt]\\'" . sisu-spine-mode))

(provide 'sisu-spine-mode)

;;

;;; sisu-spine-mode.el ends here