Skip to content

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

License

Notifications You must be signed in to change notification settings

conao3/seml-mode.el

Repository files navigation

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://img.shields.io/codacy/grade/e4d9b69ffc2143019ea3a3a729c571f1.svg?logo=codacy&style=flat-square https://img.shields.io/badge/patreon-become%20a%20patron-orange.svg?logo=patreon&style=flat-square https://img.shields.io/badge/twitter-@conao__3-blue.svg?logo=twitter&style=flat-square https://img.shields.io/badge/chat-on_slack-blue.svg?logo=slack&style=flat-square https://melpa.org/packages/seml-mode-badge.svg https://stable.melpa.org/packages/seml-mode-badge.svg

Description

Below 2 files represent the same structure. With compare 2 files, SEML is short and easy to understand for Lisp hacker.

<!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 can use Elisp freely in SEML file, I think that it has the same power as PHP. You can open files, use information that is only visible to Emacs, and use the results of Emacs hitting an external API.

seml-mode.el provides major mode for editing SEML (S-Expression Markup Language) files and utilities for you.

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

SEML Syntax

SEML syntax is very easy.

(TAG ATTRS VALUE...)
  • TAG is symbol of html tag name.
  • ATTRS is list of attribute.
    • attribute is dotted pair such as (ATTR . VALUE).
    • attribute is also allowed jade/pug like syntax sugar such as #id.class1.class2

      You do not need to specify id, but you need to put it at the beginning. And class must be separated by ..

  • VALUE is string.
  • VALUE is also allowed another SEML.
(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>"))

Complex SEML example

(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 only need to think about passing SEML list to SEML decoder. So you can use any function returning list.

Install

MELPA

seml-mode.el can install with package.el from MELPA, so sample instration code is below using leaf.el.

(prog1 "Load leaf.el"
  (add-to-list 'load-path (locate-user-emacs-file "site-lisp/leaf.el"))
  (require 'leaf)
  (leaf leaf
    :doc "Symplify your init.el configuration"
    :doc "Initialize leaf dependent packages"
    :url "https://github.com/conao3/leaf.el"
    :custom ((leaf-backend-ensure . 'package))
    :config
    (leaf package
      :custom ((package-archives . '(("org"   . "https://orgmode.org/elpa/")
                                     ("melpa" . "https://melpa.org/packages/")
                                     ("gnu"   . "https://elpa.gnu.org/packages/"))))
      :config
      (package-initialize))))

(leaf seml-mode
  :when (version<= "25.1" emacs-version)
  :ensure t)

Manual install

Put this package in your load-path, require it.

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

Note

It is already set to automatically enable seml-mode for typical seml file extensions as follows, but if you want to enable seml-mode for special extensions or for other reasons, you need to set it to an optional setting.

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

Documents

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

    Support 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

    Define single tag (without closing tag)

    (defconst seml-html-single-tags
      '(base link meta img br area param hr col option input wbr))
        

Macros

  • with-seml-elisp

    Provide environment to eval Elisp. Use ~,@(with-seml-elisp (sexp) (sexp) …)~

    (seml-mode misunderstands Elisp’s return value as a component of SEML. If you want to freely execute Elisp that does not need a return value, you need to use this macro to remove the return value from seml.)

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-encode-region-from-seml start end &optional doctype)
  • (seml-encode-sexp-from-seml sexp &optional doctype)
  • (seml-encode-string-from-seml str &optional doctype)
  • (seml-encode-buffer-from-seml &optional buf doctype)
  • (seml-encode-file-from-seml filepath &optional doctype)

Region replace functions

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

Live refresh functions

  • (seml-impatient-mode)

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)
    (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)
    (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)
    (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)

    Get SEML expression of any code in syntax highlight as specify major-mode.

    (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)

Projects

Packages build on seml-mode.

Information

Donation

I love OSS and I am dreaming of working on it as full-time job.

With your support, I will be able to spend more time at OSS!

https://c5.patreon.com/external/logo/become_a_patron_button.png

Community

All feedback and suggestions are welcome!

You can use github issues, but you can also use Slack if you want a more casual conversation.

Contribution

Feel free to send PR!

License

Affero General Public License Version 3 (AGPLv3)
Copyright (c) Naoya Yamashita - https://seml-mode.el
https://github.com/conao3/seml-mode.el/blob/master/LICENSE

Author

Contributors

  • Not yet… Now send PR and add your name!!

About

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

Resources

License

Stars

Watchers

Forks

Packages

No packages published