Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v07 init #443

Merged
merged 15 commits into from
Apr 13, 2020
Next Next commit
wip
  • Loading branch information
tlienart committed Mar 22, 2020
commit 99bae2c122d2c4d37780c5dc1a87329631af86d8
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# NEWS

This document keeps track of breaking changes and what you can do if you update and things don't work anymore.
This document keeps track of breaking changes and key new features and what you can do if you update and things don't work anymore.

Notes are in reverse chronological order.

Expand Down
7 changes: 4 additions & 3 deletions src/Franklin.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ export jd2html # = fd2html
# CONSTANTS
#

# Obtained via `dig www...`; may change over time; see check_ping
# we check in sequence, one should work... this may need to be updated
# over time.
# These are common IP addresses that we can quickly ping to see if the
# user seems online. This is used in `verify_links`. The IPs were
# obtained via `dig www...`; they may change over time; see `check_ping`
# we check in sequence, one should work if the user is online...
const IP_CHECK = (
"172.217.21.132" => "Google", # google
"140.82.118.4" => "GitHub", # github
Expand Down
87 changes: 61 additions & 26 deletions src/converter/html/blocks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -157,44 +157,30 @@ end
"""
$SIGNATURES

Process a for block.
Process a for block (for a variable iterate).
"""
function process_html_for(hs::AS, qblocks::Vector{AbstractBlock},
i::Int)::Tuple{String,Int,Int}
# check that the iterable is known
β_open = qblocks[i]
vname = β_open.vname
iname = β_open.iname

# branch for the case `{{for p in /dir1/dir2}}`
if startswith(iname, "/") # dir
# check that vname does not have brackets
startswith(vname, "(") &&
throw(HTMLBlockError("Tried to unpack a directory in a "*
"{{for ...}}. Use {{for p in /dir/}}."))
return process_html_for_dir(hs, qblocks, i, vname, iname)
end

if !haskey(LOCAL_VARS, iname)
throw(HTMLBlockError("The iterable '$iname' is not recognised. " *
"Please make sure it's defined."))
end

# try to close the for loop
if i == length(qblocks)
throw(HTMLBlockError("Could not close the conditional block " *
"starting with '$(qblocks[i].ss)'."))
end

init_idx = i
content = ""

# inbalance keeps track of whether we've managed to find a
# matching {{end}}. It increases if it sees other opening {{if..}}
# and decreases if it sees a {{end}}
inbalance = 1
while i < length(qblocks) && inbalance > 0
i += 1
inbalance += hbalance(qblocks[i])
end
# we've exhausted the candidate qblocks and not found an appropriate {{end}}
if inbalance > 0
throw(HTMLBlockError("Could not close the conditional block " *
"starting with '$(qblocks[init_idx].ss)'."))
end
# we've found the closing {{end}} and index `i`
β_close = qblocks[i]
i_close = i
i_close, β_close = get_for_body(i, qblocks)

isempty(locvar(iname)) && @goto final_step

Expand Down Expand Up @@ -241,3 +227,52 @@ function process_html_for(hs::AS, qblocks::Vector{AbstractBlock},
head = nextind(hs, to(β_close))
return convert_html(content), head, i_close
end

"""
$SIGNATURES

Process a for block (for a directory) `{{for p in /dir/subdir}}`
"""
function process_html_for_dir(hs::AS, qblocks::Vector{AbstractBlock},
i::Int, vname::String, dir::String
)::Tuple{String,Int,Int}
# filter keys of ALL_PAGE_VARS that start with dir
# XXX XXX

# retrieve the body of the loop
i_close, β_close = get_for_body(i, qblocks)
# in the body, replace instances of `$vname.$var` by `pagevar($var, $fsdir)`
for

return convert_html(content), head, i_close
end

"""
$SIGNATURES

Extract the body of a for loop, keeping track of balancing.
"""
function get_for_body(i::Int, qblocks::Vector{AbstractBlock})
# try to close the for loop
if i == length(qblocks)
throw(HTMLBlockError("Could not close the block starting with" *
"'$(qblocks[i].ss)'."))
end
init_idx = i
content = ""
# inbalance keeps track of whether we've managed to find a
# matching {{end}}. It increases if it sees other opening {{if..}}
# and decreases if it sees a {{end}}
inb = 1
while i < length(qblocks) && inb > 0
i += 1
inb += hbalance(qblocks[i])
end
# we've exhausted the candidate qblocks and not found a matching {{end}}
if inb > 0
throw(HTMLBlockError("Could not close the block starting with" *
"'$(qblocks[init_idx].ss)'."))
end
# we've found the closing {{end}} and index `i`
return i, qblocks[i]
end
3 changes: 2 additions & 1 deletion src/converter/markdown/md.jl
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,8 @@ function process_mddefs(blocks::Vector{OCBlock}, isconfig::Bool)::Nothing
if isconfig
set_vars!(GLOBAL_VARS, assignments)
else
set_vars!(LOCAL_VARS, assignments)
set_vars!(LOCAL_VARS, assignments)
ALL_PAGE_VARS[locvar("fd_rpath")] = deepcopy(LOCAL_VARS)
end
return nothing
end
27 changes: 23 additions & 4 deletions src/manager/file_utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,29 @@ Convenience function to assemble the html out of its parts.
build_page(head::String, content::String, pgfoot::String, foot::String) =
"$head\n<div class=\"$(locvar("div_content"))\">\n$content\n$pgfoot\n</div>\n$foot"

function set_cur_rpath(fpath::String)

"""
get_rpath(fpath)

Extracts the relative file system path out of the full system path to a file
currently being processed. Does not start with a path separator.
So `[some_fs_path]/blog/page.md` --> `blog/page.md`.
"""
function get_rpath(fpath::String)
root = path(ifelse(FD_ENV[:STRUCTURE] < v"0.2", :src, :folder))
cur_rpath = fpath[lastindex(root)+length(PATH_SEP)+1:end]
FD_ENV[:CUR_PATH] = cur_rpath
set_var!(LOCAL_VARS, "fd_rpath", FD_ENV[:CUR_PATH])
return fpath[lastindex(root)+length(PATH_SEP)+1:end]
end

"""
set_cur_rpath(fpath)

Takes the path to the current file and sets the `fd_rpath` local page variable
as well as the `FD_ENV[:CUR_PATH]` variable (used for conditional blocks
depending on URL for instance).
"""
function set_cur_rpath(fpath::String)
rpath = get_rpath(fpath)
FD_ENV[:CUR_PATH] = rpath
set_var!(LOCAL_VARS, "fd_rpath", rpath)
return nothing
end
29 changes: 20 additions & 9 deletions src/manager/franklin.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,24 @@ Keyword arguments:

* `clear=false`: whether to remove any existing output directory
* `verb=false`: whether to display messages
* `port=8000`: the port to use for the local server (should pick a number between 8000 and 9000)
* `port=8000`: the port to use for the local server (should pick a number
between 8000 and 9000)
* `single=false`: whether to run a single pass or run continuously
* `prerender=false`: whether to pre-render javascript (KaTeX and highlight.js)
* `nomess=false`: suppresses all messages (internal use).
* `isoptim=false`: whether we're in an optimisation phase or not (if so, links are fixed in case
of a project website, see [`write_page`](@ref).
* `no_fail_prerender=true`: whether, in a prerendering phase, ignore errors and try to produce an output
* `isoptim=false`: whether we're in an optimisation phase or not (if so,
links are fixed in case of a project website, see
[`write_page`](@ref).
* `no_fail_prerender=true`: whether, in a prerendering phase, ignore errors and
try to produce an output
* `eval_all=false`: whether to force re-evaluation of all code blocks
* `silent=false`: switch this on to suppress all output (including eval statements).
* `cleanup=true`: whether to clear environment dictionaries, see [`cleanup`](@ref).
* `on_write(pg, fd_vars)`: callback function after the page is rendered, passing as arguments
the rendered page and the page variables
* `silent=false`: switch this on to suppress all output (including eval
statements).
* `cleanup=true`: whether to clear environment dictionaries, see
[`cleanup`](@ref).
* `on_write(pg, fd_vars)`: callback function after the page is rendered,
passing as arguments the rendered page and the page
variables
"""
function serve(; clear::Bool=false,
verb::Bool=false,
Expand Down Expand Up @@ -263,7 +269,12 @@ function fd_loop(cycle_counter::Int, ::LiveServer.FileWatcher,
# anything, we just remove the file reference from the corresponding
# dictionary.
for d ∈ watched_files, (fpair, _) ∈ d
isfile(joinpath(fpair...)) || delete!(d, fpair)
fpath = joinpath(fpair...)
if !isfile(fpath)
delete!(d, fpair)
rp = get_rpath(fpath)
haskey(ALL_PAGE_VARS, rp) && delete!(ALL_PAGE_VARS, rp)
end
end
# 2) scan the input folder, if new files have been added then this will
# update the dictionaries
Expand Down
2 changes: 1 addition & 1 deletion src/regexes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const HBLOCK_ISNOTDEF_PAT = r"{{\s*i(?:s|f)n(?:ot)?def\s+([a-zA-Z_]\S*)\s*}}"
const HBLOCK_ISPAGE_PAT = r"{{\s*ispage\s+((.|\n)+?)}}"
const HBLOCK_ISNOTPAGE_PAT = r"{{\s*isnotpage\s+((.|\n)+?)}}"

const HBLOCK_FOR_PAT = r"{{\s*for\s+(\(?(?:\s*[a-zA-Z_][^\r\n\t\f\v,]*,\s*)*[a-zA-Z_]\S*\s*\)?)\s+in\s+([a-zA-Z_]\S*)\s*}}"
const HBLOCK_FOR_PAT = r"{{\s*for\s+(\(?(?:\s*[a-zA-Z_][^\r\n\t\f\v,]*,\s*)*[a-zA-Z_]\S*\s*\)?)\s+in\s+([a-zA-Z_\/]\S*)\s*}}"

const HBLOCK_TOC_PAT = r"{{\s*toc\s*}}"

Expand Down
36 changes: 34 additions & 2 deletions src/utils/vars.jl
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,48 @@ function def_LOCAL_VARS!()::Nothing
end

"""
locvar(name)

Convenience function to get the value associated with a local var.
Return `nothing` if the variable is not found.
"""
locvar(name::String) = haskey(LOCAL_VARS, name) ? LOCAL_VARS[name].first : nothing
function locvar(name::Union{Symbol,String})
name = String(name)
return haskey(LOCAL_VARS, name) ? LOCAL_VARS[name].first : nothing
end

"""
globvar(name)

Convenience function to get the value associated with a global var.
Return `nothing` if the variable is not found.
"""
globvar(name::String) = haskey(GLOBAL_VARS, name) ? GLOBAL_VARS[name].first : nothing
function globvar(name::Union{Symbol,String})
name = String(name)
return haskey(GLOBAL_VARS, name) ? GLOBAL_VARS[name].first : nothing
end


"""
Dict to keep track of all pages and their vars. Each key is a relative path
to a page, values are PageVars.
"""
const ALL_PAGE_VARS = Dict{String,PageVars}()

"""
pagevar(name, rpath)

Convenience function to get the value associated with a var available to a page
corresponding to `rpath`. So for instance if `blog/index.md` has `@def var = 0`
then this can be accessed with `pagevar("var", "blog/index")`.
"""
function pagevar(name::Union{Symbol,String}, rpath::AS)
haskey(ALL_PAGE_VARS, rpath) || return nothing
name = String(name)
haskey(ALL_PAGE_VARS[rpath], name) || return nothing
return ALL_PAGE_VARS[rpath][name]
end


"""
Keep track of seen headers. The key is the refstring, the value contains the
Expand Down
7 changes: 6 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ F.FD_ENV[:SILENT_MODE] = true
F.FD_ENV[:STRUCTURE] = v"0.1" # legacy, it's switched up in the tests.

# UTILS
println("UTILS")
println("UTILS-1")
include("utils/folder_structure.jl")
include("utils/paths_vars.jl"); include("test_utils.jl")

println("UTILS-2")
include("utils/misc.jl")
include("utils/errors.jl")
include("utils/html.jl")
Expand Down Expand Up @@ -96,6 +98,9 @@ begin
end
cd(dirname(dirname(pathof(Franklin))))

println("TEMPLATING")
include("templating/for.jl")

println("INTEGRATION")
include("integration/literate.jl")
include("integration/literate_fs2.jl")
Expand Down
42 changes: 42 additions & 0 deletions test/templating/for.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@testset "for-basic" begin
s = """
@def v1 = [1, 2, 3]
~~~
{{for v in v1}}
v: {{fill v}}
{{end}}
~~~
""" |> fd2html_td
@test isapproxstr(s, """
v: 1
v: 2
v: 3
""")

s = """
@def v1 = [[1,2], [3,4]]
~~~
{{for (a,b) in v1}}
a: {{fill a}} b: {{fill b}}
{{end}}
~~~
""" |> fd2html_td
@test isapproxstr(s, """
a: 1 b: 2
a: 3 b: 4
""")

s = """
@def v_1 = ("a"=>1, "b"=>2, "c"=>3)
~~~
{{for (a,b) in v_1}}
a: {{fill a}} b: {{fill b}}
{{end}}
~~~
""" |> fd2html_td
@test isapproxstr(s, """
a: a b: 1
a: b b: 2
a: c b: 3
""")
end