tutorial. 用.plant写作:元数据、命令与Scheme[grove-0001]

一篇Grove文章通常从title和summary命令开始。接下来的普通文本会被解释成段落;Scribble命令以at符号开头,Scheme表达式采用at-exp的括号形式。

例如,math-0001是行内代码;数学表示与计算是同一段中的链接。Grove会把两种at-exp命令都保留在当前段落里,分别渲染成代码标记和链接。

本项目的flora commands模块显式声明link命令替换Guile的同名文件系统函数,因此文章仍可直接使用link。链接目标和生成的HTML不受影响。

中西文混排的源码约定[#9]

正文和标题不在中文与英文、数字之间手动加入空格,例如写作“已有Scheme经验的读者”或“核验11个样本”。站点使用CSS的text-autospace在排版时提供间距,源码保留内容本身;不支持该属性的浏览器仍能阅读,但间距会更紧。

英文短语内部保留词间空格,例如literate programming。行内代码、代码块和示例源码保留语言需要的空格,不按正文规则整理。链接和行内代码与中文正文相接时,也不额外插入分隔空格。

example. 最小页面[#10]

下面是一个可以独立保存为plants/hello.plant的最小示例:

@use-modules[(flora commands) (language scribble commands)]

  @title{Hello}
  @summary{My first Grove page.}

  This sentence becomes a paragraph.

  @subtree{
    @title{A tree}
    This text becomes another paragraph.
  }

第一行载入本项目的命令模块。title与summary是文档元数据;空行把普通文字分成段落;subtree命令建立嵌套的tree,title为这棵tree提供标题。HTML的结构由renderer生成,作者不需要手写<p>或<h2>。

example. 命令与Scheme表达式[#11]

at-exp命令产生内容节点。例如link命令产生一个链接节点,subtree命令产生一棵嵌套的tree。Scheme表达式则能先计算,再把结果交给内容命令:

@(define greeting (string-append "Hello, " "Grove!"))

  @paragraph{The Scheme value is: @code[greeting]}

这里define在当前页面求值环境中绑定greeting;string-append返回字符串;code命令把该值作为行内代码显示。构建后的段落显示为“The Scheme value is: Hello, Grove!”。

命令和Scheme不会互相替代:命令描述要生成哪种内容,Scheme适合在读取时计算命令的输入。

普通文本如何成为段落[#12]

Grove将文本块解码为段落,因此通常直接写文字即可。需要明确段落边界、包装Scheme生成的文本或插入结构化内容时,可以使用paragraph命令。这和Scribble的写作习惯相近:作者组织语义内容,renderer决定具体标记。

definition. 一份页面源码的形状[source-shape]

页面源码由元数据、内容命令、普通文本和可选的Scheme计算组成。它保留为内容树,之后才交给分析和渲染阶段。

@use-modules[(flora commands) (language scribble commands)]

  @title{Page title}
  @summary{One-sentence description.}

  A paragraph written as ordinary text.

  @(define value (+ 20 22))
  @paragraph{The result is @code[value].}

这个例子包含模块导入、元数据、普通段落和Scheme计算;输出是一篇有标题、摘要和显示“The result is 42.”段落的页面。

组合内容树[#13]

这一页定义的source-shape subtree既有独立地址,也被首页嵌入。有关定义与嵌入的语义见组合tree。

example. 为元素指定样式[#14]

常用正文命令、link、image、subtree、transclude和数学排版命令接受#:style。符号或字符串表示命名样式;HTML将这个名字映射为class:

@paragraph[#:style 'tutorial-note]{这是一段提示。}

这是一段提示。边线与缩进来自Flora自己的CSS。

需要HTML专用属性时,使用同一个style对象携带属性:

@(define note-style
  (make-content-style 'tutorial-note
    (list (make-html-attributes '((id . "style-example") (role . "note"))))))
@paragraph[#:style note-style]{这是一段具名提示。}

这是一段具名提示。它保留role与独立的HTML ID。

class会与renderer的结构性class合并。tree被嵌入时,HTML ID会加上展示位置前缀,避免多次transclude产生重复ID;跨文章引用仍使用tree的#:id。已经由renderer生成的导航ID不能被样式覆盖。

Flora在project.scm中通过compose-html-styles组合default-html-style与flora.css。项目的style.css包含:

.tutorial-note {
  background: var(--wash);
  padding: .3lh 1ic;
}

元素样式不会改变查询事实或数学语义,文本输出也不会显示HTML属性。Scheme代码得到的元素可以交给content-with-style设置样式,无须重新构造正文或丢失源码位置。

example. 展示代码与语法高亮[#15]

纯Scheme示例可以使用code-block,并指定#:language 'scheme。代码在构建时高亮,正文是纯文本,不会被执行;原有空白、换行和Unicode字符保留。

@code-block[#:language 'scheme]{
; 中文注释与函数名也可以保留
(define (平方 x)
  (* x x))
}

上面的命令显示为:

; 中文注释与函数名也可以保留
(define (平方 x)
  (* x x))

不指定语言时显示普通代码;尚未配置的语言会产生构建warning,并显示原文。当前提供Scheme高亮,整份.plant或at-exp示例仍使用verbatim,以免被误当作Scheme。