Skip to content

Commit

Permalink
add pretty, bounds for OrderedCollections
Browse files Browse the repository at this point in the history
  • Loading branch information
joshday committed Jun 14, 2023
1 parent 9a025d7 commit 987f81f
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 28 deletions.
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Scratch = "6c6a2e73-6563-6170-7368-637461726353"

[compat]
DefaultApplication = "1"
OrderedCollections = "1.5"
Scratch = "1.1"
julia = "1"

Expand Down
50 changes: 34 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

- Open any `"text/html"`-representable object in your browser with `Page(x)` or `Tab(x)`.
- Nice syntax for writing HTML: `Cobweb.h.<tag>(children...; attributes...)`.
- `Cobweb.h.<TAB>` also autocompletes HTML5 tags.
- Lightweight, simple, and hackable.
- `Cobweb.h.<TAB>` autocompletes HTML5 tags for you.
- Great for templating/building your own `text/html` representations of Julia objects.

<br><br>

Expand Down Expand Up @@ -86,40 +86,58 @@ h.div(

## `Cobweb.h` Syntax Summary:

- `h.<tag>` creates a `Cobweb.Node`:
- `h(tag::String)` creates a `Cobweb.Node`
- `h.<tag>` is simplified syntax for `h(tag)` and you can tab-autocomplete HTML5 tags.

```julia
julia> h.div
julia> node = Cobweb.h.div
# <div></div>
```

- `Node`s are callable!
- Positional arguments add children:
- Calling a `Node` creates a copy with the specified changes.
- Positional arguments add *children*:
```julia
julia> h.div("child")
julia> node = node("child")
# <div>child</div>
```
- Keyword arguments add attributes:
- Keyword arguments add *attributes*:
```julia
julia> node = h.div(; id = "myid", class="myclass")
julia> node = node(; id = "myid", class="myclass")
# <div id="myid"></div>
```

- There's convenient syntax for appending classes as well:
```julia
julia> node."append classes"
# <div class="myclass append classes" id="myid"></div>
julia> node = node."append classes"
# <div id="myid" class="myclass append classes">child</div>
```


- `Bool`s are special-cased:
### Attributes

- `Node`s act like a mutable NamedTuple when it comes to attributes:
```julia
julia> h.div(hidden=true)
# <div hidden></div>
node = Cobweb.h.div
julia> h.div(hidden=false)
# <div></div>
node.id = "my_id"
node
# <div id="my_id"></div>
```


### Children

- `Node`s act like a `Vector` when it comes to children:

```julia
node = Cobweb.h.div
push!(node, Cobweb.h.h1("Hello!"))
node[:]
# 1-element Vector{Any}:
# <h1>Hello!</h1>
```

<br>
Expand Down
60 changes: 48 additions & 12 deletions src/Cobweb.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Should not often be used directly. See `?Cobweb.h`.
struct Node
tag::String
attrs::OrderedDict{String,String}
children::Vector
children::Vector{Any}
function Node(tag::AbstractString, attrs::AbstractDict, children::AbstractVector)
new(string(tag), OrderedDict(string(k) => string(v) for (k,v) in pairs(attrs)), collect(children))
end
Expand Down Expand Up @@ -84,7 +84,9 @@ Base.getproperty(::typeof(h), tag::Symbol) = h(string(tag))
Base.propertynames(::typeof(h)) = HTML5_TAGS

#-----------------------------------------------------------------------------# @h
HTML5_TAGS = [:a,:abbr,:address,:area,:article,:aside,:audio,:b,:base,:bdi,:bdo,:blockquote,:body,:br,:button,:canvas,:caption,:cite,:code,:col,:colgroup,:data,:datalist,:dd,:del,:details,:dfn,:dialog,:div,:dl,:dt,:em,:embed,:fieldset,:figcaption,:figure,:footer,:form,:h1,:h2,:h3,:h4,:h5,:h6,:head,:header,:hgroup,:hr,:html,:i,:iframe,:img,:input,:ins,:kbd,:label,:legend,:li,:link,:main,:map,:mark,:math,:menu,:menuitem,:meta,:meter,:nav,:noscript,:object,:ol,:optgroup,:option,:output,:p,:param,:picture,:pre,:progress,:q,:rb,:rp,:rt,:rtc,:ruby,:s,:samp,:script,:section,:select,:slot,:small,:source,:span,:strong,:style,:sub,:summary,:sup,:svg,:table,:tbody,:td,:template,:textarea,:tfoot,:th,:thead,:time,:title,:tr,:track,:u,:ul,:var,:video,:wbr]
const HTML5_TAGS = [:a,:abbr,:address,:area,:article,:aside,:audio,:b,:base,:bdi,:bdo,:blockquote,:body,:br,:button,:canvas,:caption,:cite,:code,:col,:colgroup,:data,:datalist,:dd,:del,:details,:dfn,:dialog,:div,:dl,:dt,:em,:embed,:fieldset,:figcaption,:figure,:footer,:form,:h1,:h2,:h3,:h4,:h5,:h6,:head,:header,:hgroup,:hr,:html,:i,:iframe,:img,:input,:ins,:kbd,:label,:legend,:li,:link,:main,:map,:mark,:math,:menu,:menuitem,:meta,:meter,:nav,:noscript,:object,:ol,:optgroup,:option,:output,:p,:param,:picture,:pre,:progress,:q,:rb,:rp,:rt,:rtc,:ruby,:s,:samp,:script,:section,:select,:slot,:small,:source,:span,:strong,:style,:sub,:summary,:sup,:svg,:table,:tbody,:td,:template,:textarea,:tfoot,:th,:thead,:time,:title,:tr,:track,:u,:ul,:var,:video,:wbr]

const VOID_ELEMENTS = [:area,:base,:br,:col,:command,:embed,:hr,:img,:input,:keygen,:link,:meta,:param,:source,:track,:wbr]

macro h(ex)
esc(_h(ex))
Expand All @@ -109,23 +111,57 @@ escape(x) = replace(string(x), escape_chars...)
unescape(x::AbstractString) = replace(x, reverse.(escape_chars)...)

#-----------------------------------------------------------------------------# show (html)
function Base.show(io::IO, node::Node)
p(args...) = print(io, args...)
p('<', tag(node))
for (k,v) in attrs(node)
v == "true" ? p(' ', k) : v != "false" && p(' ', k, '=', '"', v, '"')
function print_opening_tag(io::IO, o::Node; self_close::Bool = false)
print(io, '<', tag(o))
for (k,v) in attrs(o)
v == "true" ? print(io, ' ', k) : v != "false" && print(io, ' ', k, '=', '"', v, '"')
end
p('>')
for child in children(node)
showable("text/html", child) ? show(io, MIME("text/html"), child) : p(child)
end
p("</", tag(node), '>')
self_close && Symbol(tag(o)) VOID_ELEMENTS && length(children(o)) == 0 ?
print(io, " />") :
print(io, '>')
end

function Base.show(io::IO, o::Node)
p(args...) = print(io, args...)
print_opening_tag(io, o)
foreach(x -> showable("text/html", x) ? show(io, MIME("text/html"), x) : p(x), children(o))
p("</", tag(o), '>')
end

Base.show(io::IO, ::MIME"text/html", node::Node) = show(io, node)
Base.show(io::IO, ::MIME"text/xml", node::Node) = show(io, node)
Base.show(io::IO, ::MIME"application/xml", node::Node) = show(io, node)

function pretty(io::IO, o::Node; depth=get(io, :depth, 0), indent=get(io, :indent, " "), self_close = get(io, :self_close, true))
p(args...) = print(io, args...)
p(indent ^ depth)
print_opening_tag(io, o; self_close)
if length(children(o)) == 1 && !(only(o) isa Node)
x = only(o)
txt = showable("text/html", x) ? repr("text/html", x) : string(x)
if occursin('\n', txt)
println(io)
foreach(line -> p(indent ^ (depth+1), line, '\n'), lstrip.(split(txt, '\n')))
p(indent ^ depth, "</", tag(o), '>')
else
p(txt)
p("</", tag(o), '>')
end
elseif length(children(o)) > 1
child_io = IOContext(io, :depth => depth + 1, :indent => indent)
for child in children(o)
println(io)
pretty(child_io, child)
end
p('\n', indent ^ depth, "</", tag(o), '>')
end
end
function pretty(io::IO, x; depth=get(io, :depth, 0), indent=get(io, :indent, " "))
print(io, indent ^ depth)
showable("text/html", x) ? show(io, MIME("text/html"), x) : print(io, x)
end
pretty(x; kw...) = (io = IOBuffer(); pretty(io, x; kw...); String(take!(io)))

#-----------------------------------------------------------------------------# show (javascript)
struct Javascript
x::String
Expand Down

2 comments on commit 987f81f

@joshday
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request updated: JuliaRegistries/General/85596

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.5.0 -m "<description of version>" 987f81f885f7821cafbe74047e3b91c85b3673d1
git push origin v0.5.0

Please sign in to comment.