如何在 Markdown Julia 中使用内插字符串的固定宽度格式?
How can I have fixed-width format of an interpolated string in markdown Julia?
假设我有一个变量filename = "/home/jimmy/logger.log"
。我想要一些这样的降价文档:
md"""
The output is logged at `$(filename)`
"""
我知道有两种解决方法:
第一个:
Markdown.parse("The output is logged at `" * filename * "`")
第二个:
str = "The output is logged at `$(filename)`"
md"$str"
有没有办法直接告诉markdown宏在反引号解析之前做字符串插值?
这令人费解,但我想我明白了。不,没有办法让 @md_str
宏先进行字符串插值,但我在下面有一个解决方法。
我花了一段时间才意识到,当使用 f"arg"
或 f`arg`
语法调用 @f_str
或 @f_cmd
宏时,它接收原始字符串 raw"arg"
作为参数而不是表达式 :("arg")
否则宏会得到:
julia> macro showarg_str(x) show(x) end;
julia> showarg"text$var"
"text$var"
julia> @showarg_str("text$var")
:("text$(var)")
此行为 is defined 在 julia-paser.scm
解析器中。
但是像 md"$expr"
这样的代码显示的是计算后的 expr
而不是原始字符串 raw"$expr"
!这是怎么回事?事实证明 $expr
在通过 Markdown 模块中的自定义代码解析为 Markdown 期间被解析为表达式;这是 largely implemented in interp.jl
. According to the documentation 这是有意而为的,以便 Markdown 树存储表达式而不是评估这些表达式的字符串。从理论上讲,这有助于高级自定义功能。实际上,这让我头疼。
现在,使用 Markdown.parse
似乎可以如您所愿。因为作为常规函数而不是宏,Markdown.parse
接收内插字符串,而不是原始字符串或表达式:
julia> Markdown.parse("The output is logged at `$(filename)`") |> show
The output is logged at `/home/jimmy/logger.log`
但是,由于 $
处理在 Markdown.parse
中再次发生,所以有 undesirable/unintuitive 行为是这样的:
julia> Markdown.parse(raw"$filename") |> show
:filename
以下内容未记录,但我认为改变此行为的最佳方法是在 Markdown.flavors
中定义和自定义新的 Markdown 风格,然后使用此风格作为参数调用 Markdown.parse
.在 flavor 的配置中,我们只需要停止在解析期间调用 blockinterp
和 interp
。可以这样做:
let
config = deepcopy(Markdown.flavors[:julia])
filter!(!=(Markdown.blockinterp), config.regular)
filter!(!=(Markdown.interp), get(config.inner, '$', []))
Markdown.flavors[:julia_nointerp] = config
end
为了允许 Markdown 模块在此配置之后而不是之前加载,我们可以将其放入一个方便的函数中:
function md(str, flavor=:julia_nointerp)
@isdefined(Markdown) || error("the Markdown module must be loaded. Try `using Markdown`.")
if flavor == :julia_nointerp && !haskey(Markdown.flavors, :julia_nointerp)
config = deepcopy(Markdown.flavors[:julia])
filter!(!=(Markdown.blockinterp), config.regular)
filter!(!=(Markdown.interp), get(config.inner, '$', []))
Markdown.flavors[:julia_nointerp] = config
end
return Markdown.parse(str; flavor=flavor)
end
现在您可以调用 md("markdown code")
来使用标准字符串插值进行 Markdown 解析,而不是 md"markdown code"
执行的特殊 $
解析:
julia> filename = "/home/jimmy/logger.log";
julia> md("The output is logged at `$(filename)`") |> show
The output is logged at `/home/jimmy/logger.log`
julia> md("The output is logged at `$(filename)`") |> typeof
Markdown.MD
julia> md("The output is logged at `$(filename)`").content
1-element Array{Any,1}:
Markdown.Paragraph(Any["The output is logged at ", Markdown.Code("", "/home/jimmy/logger.log")])
P.S.: 我不建议将 md
变成宏或尝试修改 @md_str
宏,因为:(1) 一个函数就足够了,并且 (2)使用函数意味着用户可以确定在将参数传递给 md
.
之前会发生标准字符串插值
假设我有一个变量filename = "/home/jimmy/logger.log"
。我想要一些这样的降价文档:
md"""
The output is logged at `$(filename)`
"""
我知道有两种解决方法:
第一个:
Markdown.parse("The output is logged at `" * filename * "`")
第二个:
str = "The output is logged at `$(filename)`"
md"$str"
有没有办法直接告诉markdown宏在反引号解析之前做字符串插值?
这令人费解,但我想我明白了。不,没有办法让 @md_str
宏先进行字符串插值,但我在下面有一个解决方法。
我花了一段时间才意识到,当使用 f"arg"
或 f`arg`
语法调用 @f_str
或 @f_cmd
宏时,它接收原始字符串 raw"arg"
作为参数而不是表达式 :("arg")
否则宏会得到:
julia> macro showarg_str(x) show(x) end;
julia> showarg"text$var"
"text$var"
julia> @showarg_str("text$var")
:("text$(var)")
此行为 is defined 在 julia-paser.scm
解析器中。
但是像 md"$expr"
这样的代码显示的是计算后的 expr
而不是原始字符串 raw"$expr"
!这是怎么回事?事实证明 $expr
在通过 Markdown 模块中的自定义代码解析为 Markdown 期间被解析为表达式;这是 largely implemented in interp.jl
. According to the documentation 这是有意而为的,以便 Markdown 树存储表达式而不是评估这些表达式的字符串。从理论上讲,这有助于高级自定义功能。实际上,这让我头疼。
现在,使用 Markdown.parse
似乎可以如您所愿。因为作为常规函数而不是宏,Markdown.parse
接收内插字符串,而不是原始字符串或表达式:
julia> Markdown.parse("The output is logged at `$(filename)`") |> show
The output is logged at `/home/jimmy/logger.log`
但是,由于 $
处理在 Markdown.parse
中再次发生,所以有 undesirable/unintuitive 行为是这样的:
julia> Markdown.parse(raw"$filename") |> show
:filename
以下内容未记录,但我认为改变此行为的最佳方法是在 Markdown.flavors
中定义和自定义新的 Markdown 风格,然后使用此风格作为参数调用 Markdown.parse
.在 flavor 的配置中,我们只需要停止在解析期间调用 blockinterp
和 interp
。可以这样做:
let
config = deepcopy(Markdown.flavors[:julia])
filter!(!=(Markdown.blockinterp), config.regular)
filter!(!=(Markdown.interp), get(config.inner, '$', []))
Markdown.flavors[:julia_nointerp] = config
end
为了允许 Markdown 模块在此配置之后而不是之前加载,我们可以将其放入一个方便的函数中:
function md(str, flavor=:julia_nointerp)
@isdefined(Markdown) || error("the Markdown module must be loaded. Try `using Markdown`.")
if flavor == :julia_nointerp && !haskey(Markdown.flavors, :julia_nointerp)
config = deepcopy(Markdown.flavors[:julia])
filter!(!=(Markdown.blockinterp), config.regular)
filter!(!=(Markdown.interp), get(config.inner, '$', []))
Markdown.flavors[:julia_nointerp] = config
end
return Markdown.parse(str; flavor=flavor)
end
现在您可以调用 md("markdown code")
来使用标准字符串插值进行 Markdown 解析,而不是 md"markdown code"
执行的特殊 $
解析:
julia> filename = "/home/jimmy/logger.log";
julia> md("The output is logged at `$(filename)`") |> show
The output is logged at `/home/jimmy/logger.log`
julia> md("The output is logged at `$(filename)`") |> typeof
Markdown.MD
julia> md("The output is logged at `$(filename)`").content
1-element Array{Any,1}:
Markdown.Paragraph(Any["The output is logged at ", Markdown.Code("", "/home/jimmy/logger.log")])
P.S.: 我不建议将 md
变成宏或尝试修改 @md_str
宏,因为:(1) 一个函数就足够了,并且 (2)使用函数意味着用户可以确定在将参数传递给 md
.