Skip to content

conao3/seml-mode.el

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

seml-mode.el

https://raw.githubusercontent.com/conao3/files/master/blob/headers/png/seml-mode.el.png

https://img.shields.io/github/license/conao3/seml-mode.el.svg?style=flat-square https://img.shields.io/github/tag/conao3/seml-mode.el.svg?style=flat-square https://github.com/conao3/seml-mode.el/workflows/Main%20workflow/badge.svg https://melpa.org/packages/seml-mode-badge.svg https://stable.melpa.org/packages/seml-mode-badge.svg

Overview

seml-mode.el provides a major mode for editing SEML (S-Expression Markup Language) files. SEML lets you write HTML using familiar Lisp S-expressions instead of angle brackets.

The two files below represent the same structure. SEML is concise and intuitive for Lisp users:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <title>sample page</title>
    <link rel="stylesheet" href="sample1.css"/>
  </head>
  <body>
    <h1>sample</h1>
    <p>
      text sample
    </p>
  </body>
</html>
(html ((lang . "en"))
  (head nil
    (meta ((charset . "utf-8")))
    (title nil "sample page")
    (link ((rel . "stylesheet") (href . "sample1.css"))))
  (body nil
    (h1 nil "sample")
    (p nil "text sample")))

https://raw.githubusercontent.com/conao3/files/master/blob/seml-mode.el/simple-seml.png

Since SEML supports full Elisp evaluation within templates, you can open files, access Emacs internals, call external APIs, and generate dynamic content - similar to what PHP offers for HTML templating.

https://raw.githubusercontent.com/conao3/files/master/blob/seml-mode.el/complex-seml.png

SEML Syntax

The basic syntax is straightforward:

(TAG ATTRS VALUE...)
  • TAG is a symbol representing an HTML tag name
  • ATTRS is a list of attributes as dotted pairs (ATTR . VALUE)
    • Attributes also support Jade/Pug-style shorthand like #id.class1.class2
    • The id must come first if specified, and classes are separated by .
  • VALUE is a string or another nested SEML expression
(cort-deftest seml-mode:/simple-jade
  (:string= (seml-decode-seml-from-sexp '(h1 ("#header.class1.class2") "sample"))
            "<h1 id=\"header\" class=\"class1 class2\">sample</h1>"))

(cort-deftest seml-mode:/simple-jade2
  (:string= (seml-decode-seml-from-sexp '(h1 ("#header.class1") "sample"))
            "<h1 id=\"header\" class=\"class1\">sample</h1>"))

(cort-deftest seml-mode:/simple-jade3
  (:string= (seml-decode-seml-from-sexp '(h1 ("class1") "sample"))
            "<h1 class=\"class1\">sample</h1>"))

Dynamic Content Example

Since SEML is just Elisp, you can generate content programmatically. Pass any SEML list to the decoder:

(cort-deftest seml-mode:/simple-ul
  (:string= (seml-decode-seml-from-sexp
             `(ul nil
                  ,@(mapcar (lambda (x)
                             `(li nil ,(format "item-%s" x)))
                           (number-sequence 1 5))))
            "<ul>
  <li>item-1</li>
  <li>item-2</li>
  <li>item-3</li>
  <li>item-4</li>
  <li>item-5</li>
</ul>"))

You can use any function that returns a list.

Installation

Requires Emacs 25.1 or later.

From MELPA

;; Using package.el
(package-install 'seml-mode)

;; Using use-package
(use-package seml-mode
  :ensure t)

;; Using leaf.el
(leaf seml-mode
  :ensure t)

Manual Installation

Add this package to your load-path and require it:

(add-to-list 'load-path
             (locate-user-emacs-file "site-lisp/seml-mode.el"))
(require 'seml-mode)

Note

The mode automatically activates for .seml files. For custom extensions, add:

(add-to-list 'auto-mode-alist '("\\.seml\\'" . seml-mode))
(add-to-list 'interpreter-mode-alist '("seml" . seml-mode))

API Reference

Customizable Variables

  • seml-mode-hook
  • seml-import-dir
  • seml-live-refresh-interval
  • seml-live-refresh-url-variable
  • seml-live-refresh-url-quety

Constants

  • seml-mode-keywords - Supported HTML5 tags:
    (defconst seml-mode-keywords
      '(html
        head title base link meta style
        script noscript
        body section nav article aside hgroup header footer address
        h1 h2 h3 h4 h5 h6
        p hr pre backquote ol ul li
        dl dt dd figure figcaption div main
        a em strong small s cite q dfn addr time code var
        samp kbd sub sup i b mark ruby rt rpbdo span br wbr
        ins del
        img iframe embed object param
        video audio source canvas map area
        table caption colgroup col tbody thead tfoot tr td th
        form fieldset legend label input button select
        datalist optgroup option textarea keygen output progress meter
        details summary command menu
    
        ;; libxml-parse keywords
        comment top))
        
  • seml-html-single-tags - Self-closing tags:
    (defconst seml-html-single-tags
      '(base link meta img br area param hr col option input wbr))
        

Macros

  • with-seml-elisp - Evaluate Elisp without returning a value to SEML. Use ,@(with-seml-elisp (sexp) (sexp) ...) when you need to execute Elisp that should not contribute to the SEML output.

Functions

Encode Functions (HTML to SEML)

  • (seml-encode-region-from-html pointmin pointmax)
  • (seml-encode-string-from-html str)
  • (seml-encode-buffer-from-html &optional buf)
  • (seml-encode-file-from-html filepath)

Decode Functions (SEML to HTML)

  • (seml-decode-region-from-seml start end &optional doctype)
  • (seml-decode-sexp-from-seml sexp &optional doctype)
  • (seml-decode-string-from-seml str &optional doctype)
  • (seml-decode-buffer-from-seml &optional buf doctype)
  • (seml-decode-file-from-seml filepath &optional doctype)

Region Replace Functions

  • (seml-replace-region-from-html)
  • (seml-replace-region-from-seml)

Live Refresh

  • (seml-impatient-mode) - Enable live browser preview as you edit.

Utility Functions

  • (seml-indent-function indent-point state)
  • (seml-to-string sexp)
  • (seml-pp sexp &optional stream return-p)
  • (seml-xpath xpath sexp &optional without-top) - Navigate SEML with XPath-like paths:
    (cort-deftest seml-test:simple-xpath
      (:equal
       (seml-xpath '(html head link)
                   '(html ((lang . "en"))
                          (head nil
                                (meta ((charset . "utf-8")))
                                (title nil
                                       "sample page")
                                (link ((rel . "stylesheet") (href . "sample1.css")))
                                (link ((rel . "stylesheet") (href . "sample2.css"))))
                          (body nil
                                (h1 nil
                                    "sample")
                                (p nil
                                   "sample"
                                   "text sample"))))
       '((link
          ((rel . "stylesheet")
           (href . "sample1.css")))
         (link
          ((rel . "stylesheet")
           (href . "sample2.css"))))))
        
  • (seml-xpath-single xpath sexp &optional without-top) - Return first matching element:
    (cort-deftest seml-test:/simple-xpath-single
      (:equal
       (seml-xpath-single '(html body)
         '(html ((lang . "en"))
                (head nil
                      (meta ((charset . "utf-8")))
                      (title nil
                             "sample page")
                      (link ((rel . "stylesheet") (href . "sample1.css")))
                      (link ((rel . "stylesheet") (href . "sample2.css"))))
                (body nil
                      (h2 nil "sample-1")
                      (h2 nil "sample-2")
                      (h2 nil "sample-3")
                      (p nil
                         "sample"
                         "text sample"))))
       '(body nil
              (h2 nil "sample-1")
              (h2 nil "sample-2")
              (h2 nil "sample-3")
              (p nil
                 "sample"
                 "text sample"))))
        
  • (seml-xpath-without-top xpath sexp) - Return matches without the top element:
    (cort-deftest seml-test:/simple-xpath-without-top
      (:equal
       (seml-xpath '(html body h2)
         '(html ((lang . "en"))
                (head nil
                      (meta ((charset . "utf-8")))
                      (title nil
                             "sample page")
                      (link ((rel . "stylesheet") (href . "sample1.css")))
                      (link ((rel . "stylesheet") (href . "sample2.css"))))
                (body nil
                      (h2 nil "sample-1")
                      (h2 nil "sample-2")
                      (h2 nil "sample-3")
                      (p nil
                         "sample"
                         "text sample")))
         t)
       '(("sample-1")
         ("sample-2")
         ("sample-3"))))
        
  • (seml-xpath-single-without-top xpath sexp)
  • (seml-htmlize majormode codestr &optional noindentp formatfn) - Generate SEML with syntax highlighting:
    (cort-deftest seml-mode:/simple-htmlize
      (:equal (seml-htmlize 'emacs-lisp-mode "(leaf real-auto-save
      :ensure t
      :custom ((real-auto-save-interval . 0.3))
      :commands real-auto-save-mode
      :hook (find-file-hook . real-auto-save-mode))")
              '(pre nil "
    ("
                    (span ((class . "keyword")) "leaf")
                    " real-auto-save
      "
                    (span ((class . "builtin")) ":ensure")
                    " t
      "
                    (span ((class . "builtin")) ":custom")
                    " ((real-auto-save-interval . 0.3))
      "
                    (span ((class . "builtin")) ":commands")
                    " real-auto-save-mode
      "
                    (span ((class . "builtin")) ":hook")
                    " (find-file-hook . real-auto-save-mode))")))
        
  • (seml-import path)
  • (seml-expand-url path baseurl)

Major Mode

  • (seml-mode)

Related Projects

Contributing

Contributions are welcome. Please feel free to submit issues and pull requests on GitHub.

License

AGPLv3. See LICENSE for details.

Author

Naoya Yamashita (conao3)

About

Major mode for editing SEML (S-Expression Markup Language) files

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •