Compare commits
3 commits
Author | SHA1 | Date | |
---|---|---|---|
31a825b033 | |||
a73af8d936 | |||
fa7fc1605e |
7 changed files with 114 additions and 37 deletions
70
.forgejo/workflows/ci.yml
Normal file
70
.forgejo/workflows/ci.yml
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
name: 'CI'
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: docker
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
lisp:
|
||||||
|
- sbcl-bin
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Restore cache
|
||||||
|
id: restore-cache
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.roswell
|
||||||
|
/usr/local/bin/ros
|
||||||
|
/usr/local/etc/roswell/
|
||||||
|
qlfile
|
||||||
|
qlfile.lock
|
||||||
|
.qlot
|
||||||
|
~/.cache/common-lisp/
|
||||||
|
key: roswell-${{ runner.os }}-${{ matrix.lisp }}-${{ hashFiles('qlfile', 'qlfile.lock', '*.asd') }}
|
||||||
|
|
||||||
|
- name: Install Roswell
|
||||||
|
if: steps.restore-cache.outputs.cache-hit != 'true'
|
||||||
|
env:
|
||||||
|
LISP: ${{ matrix.lisp }}
|
||||||
|
run: |
|
||||||
|
curl -L https://raw.githubusercontent.com/roswell/roswell/master/scripts/install-for-ci.sh | sh
|
||||||
|
|
||||||
|
- name: Install Qlot
|
||||||
|
if: steps.restore-cache.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
ros install fukamachi/qlot
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
if: steps.restore-cache.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
PATH="~/.roswell/bin:$PATH"
|
||||||
|
qlot install
|
||||||
|
qlot exec ros install hsx
|
||||||
|
|
||||||
|
- name: Save cache
|
||||||
|
id: save-cache
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
if: steps.restore-cache.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.roswell
|
||||||
|
/usr/local/bin/ros
|
||||||
|
/usr/local/etc/roswell/
|
||||||
|
qlfile
|
||||||
|
qlfile.lock
|
||||||
|
.qlot
|
||||||
|
~/.cache/common-lisp/
|
||||||
|
key: ${{ steps.restore-cache.outputs.cache-primary-key }}
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: .qlot/bin/rove hsx.asd
|
|
@ -1,4 +1,4 @@
|
||||||
name: 'test'
|
name: 'CI'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
@ -7,7 +7,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
strategy:
|
25
README.md
25
README.md
|
@ -1,6 +1,7 @@
|
||||||
# HSX
|
# HSX
|
||||||
|
|
||||||
HSX (Hypertext S-expression) is a simple yet powerful HTML5 generation library for Common Lisp.
|
HSX (Hypertext S-expression) is a simple and powerful HTML (Living Standard)
|
||||||
|
generation library for Common Lisp.
|
||||||
|
|
||||||
This project is a fork of [ailisp/flute](https://github.com/ailisp/flute/).
|
This project is a fork of [ailisp/flute](https://github.com/ailisp/flute/).
|
||||||
|
|
||||||
|
@ -8,13 +9,16 @@ This project is a fork of [ailisp/flute](https://github.com/ailisp/flute/).
|
||||||
|
|
||||||
This software is still in ALPHA quality. The APIs are likely to change.
|
This software is still in ALPHA quality. The APIs are likely to change.
|
||||||
|
|
||||||
Please check the [release notes](https://github.com/skyizwhite/hsx/releases) for updates.
|
Please check the [release notes](https://code.skyizwhite.dev/paku/hsx/releases)
|
||||||
|
for updates.
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
### Basic Usage
|
### Basic Usage
|
||||||
|
|
||||||
Use the `hsx` macro to create HTML elements. Attributes are specified using a property list after the element name, and child elements are nested directly inside.
|
Use the `hsx` macro to create HTML elements. Attributes are specified using a
|
||||||
|
property list after the element name, and child elements are nested directly
|
||||||
|
inside.
|
||||||
|
|
||||||
```lisp
|
```lisp
|
||||||
(hsx
|
(hsx
|
||||||
|
@ -32,7 +36,8 @@ This generates:
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
To convert an HSX object into an HTML string, use the `render-to-string` function:
|
To convert an HSX object into an HTML string, use the `render-to-string`
|
||||||
|
function:
|
||||||
|
|
||||||
```lisp
|
```lisp
|
||||||
(render-to-string
|
(render-to-string
|
||||||
|
@ -43,7 +48,8 @@ To convert an HSX object into an HTML string, use the `render-to-string` functio
|
||||||
|
|
||||||
HSX allows you to embed Common Lisp forms directly within your HTML structure.
|
HSX allows you to embed Common Lisp forms directly within your HTML structure.
|
||||||
|
|
||||||
When working with HSX elements inside embedded Lisp forms, you should use the `hsx` macro again.
|
When working with HSX elements inside embedded Lisp forms, you should use the
|
||||||
|
`hsx` macro again.
|
||||||
|
|
||||||
```lisp
|
```lisp
|
||||||
(hsx
|
(hsx
|
||||||
|
@ -76,7 +82,8 @@ This might generate:
|
||||||
|
|
||||||
### Using Fragments
|
### Using Fragments
|
||||||
|
|
||||||
To group multiple elements without adding an extra wrapper, use the fragment `<>`.
|
To group multiple elements without adding an extra wrapper, use the fragment
|
||||||
|
`<>`.
|
||||||
|
|
||||||
```lisp
|
```lisp
|
||||||
(hsx
|
(hsx
|
||||||
|
@ -96,7 +103,9 @@ This generates:
|
||||||
|
|
||||||
### Creating Components
|
### Creating Components
|
||||||
|
|
||||||
You can define reusable components using the `defcomp` macro. Component names must begin with a tilde (`~`). Properties should be declared using `&key`, `&rest`, or both. The body must return an HSX element.
|
You can define reusable components using the `defcomp` macro. Component names
|
||||||
|
must begin with a tilde (`~`). Properties should be declared using `&key`,
|
||||||
|
`&rest`, or both. The body must return an HSX element.
|
||||||
|
|
||||||
```lisp
|
```lisp
|
||||||
(defcomp ~card (&key title children)
|
(defcomp ~card (&key title children)
|
||||||
|
@ -140,5 +149,3 @@ This project is licensed under the MIT License.
|
||||||
© 2024 skyizwhite
|
© 2024 skyizwhite
|
||||||
|
|
||||||
© 2018 Bo Yao
|
© 2018 Bo Yao
|
||||||
|
|
||||||
Feel free to contribute to the project and report any issues or feature requests on the [GitHub repository](https://github.com/skyizwhite/hsx).
|
|
||||||
|
|
2
hsx.asd
2
hsx.asd
|
@ -1,6 +1,6 @@
|
||||||
(defsystem "hsx"
|
(defsystem "hsx"
|
||||||
:version "0.4.0"
|
:version "0.4.0"
|
||||||
:description "Hypertext S-expression"
|
:description "Simple and powerful HTML generation library."
|
||||||
:author "skyizwhite, Bo Yao"
|
:author "skyizwhite, Bo Yao"
|
||||||
:maintainer "skyizwhite <paku@skyizwhite.dev>"
|
:maintainer "skyizwhite <paku@skyizwhite.dev>"
|
||||||
:license "MIT"
|
:license "MIT"
|
||||||
|
|
6
qlfile
6
qlfile
|
@ -1,6 +1,6 @@
|
||||||
ql alexandria
|
ql alexandria
|
||||||
ql cl-str
|
ql cl-str
|
||||||
|
|
||||||
github rove fukamachi/rove
|
git rove https://github.com/fukamachi/rove
|
||||||
github dissect Shinmera/dissect ; workaround
|
git dissect https://github.com/Shinmera/dissect ; workaround
|
||||||
ql mstrings
|
git mstrings https://git.sr.ht/~shunter/mstrings
|
||||||
|
|
24
qlfile.lock
24
qlfile.lock
|
@ -1,24 +1,24 @@
|
||||||
("quicklisp" .
|
("quicklisp" .
|
||||||
(:class qlot/source/dist:source-dist
|
(:class qlot/source/dist:source-dist
|
||||||
:initargs (:distribution "https://beta.quicklisp.org/dist/quicklisp.txt" :%version :latest)
|
:initargs (:distribution "https://beta.quicklisp.org/dist/quicklisp.txt" :%version :latest)
|
||||||
:version "2023-10-21"))
|
:version "2024-10-12"))
|
||||||
("alexandria" .
|
("alexandria" .
|
||||||
(:class qlot/source/ql:source-ql
|
(:class qlot/source/ql:source-ql
|
||||||
:initargs (:%version :latest)
|
:initargs (:%version :latest)
|
||||||
:version "ql-2023-10-21"))
|
:version "ql-2024-10-12"))
|
||||||
("cl-str" .
|
("cl-str" .
|
||||||
(:class qlot/source/ql:source-ql
|
(:class qlot/source/ql:source-ql
|
||||||
:initargs (:%version :latest)
|
:initargs (:%version :latest)
|
||||||
:version "ql-2023-10-21"))
|
:version "ql-2024-10-12"))
|
||||||
("rove" .
|
("rove" .
|
||||||
(:class qlot/source/github:source-github
|
(:class qlot/source/git:source-git
|
||||||
:initargs (:repos "fukamachi/rove" :ref nil :branch nil :tag nil)
|
:initargs (:remote-url "https://github.com/fukamachi/rove")
|
||||||
:version "github-cacea7331c10fe9d8398d104b2dfd579bf7ea353"))
|
:version "git-cacea7331c10fe9d8398d104b2dfd579bf7ea353"))
|
||||||
("dissect" .
|
("dissect" .
|
||||||
(:class qlot/source/github:source-github
|
(:class qlot/source/git:source-git
|
||||||
:initargs (:repos "Shinmera/dissect" :ref nil :branch nil :tag nil)
|
:initargs (:remote-url "https://github.com/Shinmera/dissect")
|
||||||
:version "github-a70cabcd748cf7c041196efd711e2dcca2bbbb2c"))
|
:version "git-a70cabcd748cf7c041196efd711e2dcca2bbbb2c"))
|
||||||
("mstrings" .
|
("mstrings" .
|
||||||
(:class qlot/source/ql:source-ql
|
(:class qlot/source/git:source-git
|
||||||
:initargs (:%version :latest)
|
:initargs (:remote-url "https://git.sr.ht/~shunter/mstrings")
|
||||||
:version "ql-2023-10-21"))
|
:version "git-7a94c070141c7cd03bbd3648b17724c3bf143393"))
|
||||||
|
|
16
src/dsl.lisp
16
src/dsl.lisp
|
@ -16,7 +16,7 @@
|
||||||
"Detect HSX elements and automatically import them."
|
"Detect HSX elements and automatically import them."
|
||||||
(detect-elements form))
|
(detect-elements form))
|
||||||
|
|
||||||
(defun get-builtin-symbol (sym)
|
(defun detect-builtin-symbol (sym)
|
||||||
(multiple-value-bind (builtin-sym kind)
|
(multiple-value-bind (builtin-sym kind)
|
||||||
(find-symbol (string sym) :hsx/builtin)
|
(find-symbol (string sym) :hsx/builtin)
|
||||||
(and (eq kind :external) builtin-sym)))
|
(and (eq kind :external) builtin-sym)))
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
(defun start-with-tilde-p (sym)
|
(defun start-with-tilde-p (sym)
|
||||||
(string= "~" (subseq (string sym) 0 1)))
|
(string= "~" (subseq (string sym) 0 1)))
|
||||||
|
|
||||||
(defun get-component-symbol (sym)
|
(defun detect-component-symbol (sym)
|
||||||
(and (start-with-tilde-p sym) sym))
|
(and (start-with-tilde-p sym) sym))
|
||||||
|
|
||||||
(defun detect-elements (form)
|
(defun detect-elements (form)
|
||||||
|
@ -34,8 +34,8 @@
|
||||||
(well-formed-p (listp tail))
|
(well-formed-p (listp tail))
|
||||||
(detected-sym (and (symbolp head)
|
(detected-sym (and (symbolp head)
|
||||||
(not (keywordp head))
|
(not (keywordp head))
|
||||||
(or (get-builtin-symbol head)
|
(or (detect-builtin-symbol head)
|
||||||
(get-component-symbol head)))))
|
(detect-component-symbol head)))))
|
||||||
(if (and well-formed-p detected-sym)
|
(if (and well-formed-p detected-sym)
|
||||||
(cons detected-sym
|
(cons detected-sym
|
||||||
(mapcar (lambda (sub-form)
|
(mapcar (lambda (sub-form)
|
||||||
|
@ -75,16 +75,16 @@
|
||||||
(defhsx ,name ,(make-keyword name))))
|
(defhsx ,name ,(make-keyword name))))
|
||||||
|
|
||||||
(defmacro defcomp (~name props &body body)
|
(defmacro defcomp (~name props &body body)
|
||||||
"Define a function component for HSX.
|
"Define an HSX function component.
|
||||||
The component name must start with a tilde (~).
|
The component name must start with a tilde (~).
|
||||||
Properties must be declared using &key, &rest, or both.
|
Component properties must be declared using &key, &rest, or both.
|
||||||
The body must return an HSX element."
|
The body of the component must produce a valid HSX element."
|
||||||
(unless (start-with-tilde-p ~name)
|
(unless (start-with-tilde-p ~name)
|
||||||
(error "The component name must start with a tilde (~~)."))
|
(error "The component name must start with a tilde (~~)."))
|
||||||
(unless (or (null props)
|
(unless (or (null props)
|
||||||
(member '&key props)
|
(member '&key props)
|
||||||
(member '&rest props))
|
(member '&rest props))
|
||||||
(error "Component properties must be declared with either &key, &rest, or both."))
|
(error "Component properties must be declared using &key, &rest, or both."))
|
||||||
(let ((%name (symbolicate '% ~name)))
|
(let ((%name (symbolicate '% ~name)))
|
||||||
`(eval-when (:compile-toplevel :load-toplevel :execute)
|
`(eval-when (:compile-toplevel :load-toplevel :execute)
|
||||||
(defun ,%name ,props
|
(defun ,%name ,props
|
||||||
|
|
Loading…
Reference in a new issue