一门用于描述敏捷看板的简易标记语言。
A simple markup language for describe agility board.
看板是一种管理工作任务的方法,经常被用在敏捷的团队管理实践中。
Catalpa实现一门简易的标记语言,可以直观的描述看板内容,通过实现查看器和编辑器,可以支持可视化查看和编辑。
在尝试演示和构建之前,你需要先安装nodejs,当前项目使用pnpm管理,请先安装pnpm。
下载代码
git clone https://github.com/mrchar/catalpa.git
安装依赖
cd catalpa
pnpm install
尝试功能
pnpm dev
构建项目
pnpm build
首先让我们来快速了解一下如何使用Catalpa语法描述我们的待办事项。
---
title: 团队待办事项
tags: 工作, 游戏, 计划, 执行, 检查, 回顾, 工作 alias Job
members: 小王, 小李, 小周, 小潘, 小潘 alias Tim, All alias 所有人
phases: Plan, Doing, Check, Action, Done, Done alias OK
---
计划中:
周末组织团队到一个风景优美的地方转转 tag:团建 tag:摄影 member:所有人 ddl:2023/1/15 begin:2023/1/12 phase:Plan
选择一个合适的目的地 #计划 @小王 !2023/1/12 [2023/1/12, 2023/1/12] :Done
计划出行的方式和路线 #计划 #交通 @小王 !2023/1/13 ^2023/1/13 :Doing
整理出行要带的物品清单 #计划 @小李 !2023/1/13 :Done
执行出行时间计划 #计划 @小王 !2023/1/13
1. 整理一个可行的时间表
2. 通知所有人
行程中 #执行 @小周 !2023/1/15
返回后整理游玩的照片并分享给团队 #回顾 @Tim !2023/1/20
已完成:
第一季度绩效报告会议 #会议 #里程碑 !2023/1/10 :Done
catalpa 语法按行解析, 每解析一行创建一项任务,每个任务由任务描述和附加标记组成。
当解析到第一个附加标记的时候,说明任务描述已经结束,任务描述不包含首尾的空白字符。
附加标记的基本语法为标记类型:标记内容
,使用:
连接标记的类型标识和标记内容。
当解析到下一个标记的时候,表示上一个标记内容已经结束,标记内容不包含首尾的空白字符。
另外,一些常见的附加标记支持使用简写,例如tag:
可以简写为#
,member:
可以简写为@
,
这些符号都是在其他领域广泛应用的,几乎不需要解释也可以被新手理解。
catalpa 语法使用缩进标识各项之间的层级关系,解析任务时,将计算当前任务的缩进字符数, 如果缩进数为0,则当前任务是一条主任务,如果缩进数不为 0,当前任务也将属于上方遇到的第 1 个缩进数小于当前任务缩进数的记录。
任务可以使用分组标记进行分组,当一行内容只包含非空白字符且以:
结束时,:
前的非空白字符被识别为分组名称。
那么随后的所有任务将被包含在这个分组中,直到出现新的分组标记结束。
如果希望任务不在任何分组中,那么这个任务必须在所有分组标记的前面记录。
例如:
task
group1:
task1
subtask11
task2
group2:
task3
subtask31
上面的记录中task
不属于任何分组,task1
和task2
属于group1
,task3
属于group2
。
附加标记以空白字符进行分割,如果你的标记内容中包含标记关键字或者缩写,那么在关键字或者缩写前面不要有紧邻着的空白字符,因为这样会解析错误。
关键字 | 缩写 | 解释 |
---|---|---|
tag | # | 标签 |
member | @ | 成员 |
ddl | ! | 截止日期 |
begin | ^ | 开始时间 |
end | $ | 完成时间 |
phase | : | 当前阶段 |
tag 用来对当前任务进行标记, 在未来的查看器或编辑器中,你可以通过这些标签对任务进行分组、排序或者过滤, 可以轻松的使用 tag 隐藏掉不关心的任务。
member
member 用于标记当前任务的参与者。
如果你在 mail list 中使用 catalpa 语法,可以使用 email 地址来标记参与者,例如@[email protected]
。
如果你在 github 的自述文档中使用 catalpa 语法,你可以使用 github 用户名标记参与者, 例如@mrchar
。
或者你可以在你的系统中对参与者进行解析,例如你可以定义参与者标记的语法为name|uid
,这样即使在你的系统中用户名是可以重复的,你也可以使用uid
检索到对应的成员。
ddl
ddl 表示任务的截至日期,在计划工作的时候我们通常不是那么关心开始和结束的时间,反而是更在乎任务的截至日期。
period
begin 和 end 分别用于表示任务的开始和结束时间,通常我们不需要标记任务的开始和结束时间, 但是如果你特别在乎开始和结束的时间,也可以使用这两个标记明确表示任务的开始和结束时间。
因为开始和结束往往是成对存在的,为了更加方便地表示任务的开始和结束时间, 我创建了一个例外的缩写,你可以将开始和结束时间使用尖括号包裹起来。
例如[2023/1/12, 2023/1/13]
表示任务在2023/1/12日开始,在2023/1/13日结束,当然就算是在任务结束之前你也可以这种方式表示开始时间,例如[2023/1/12, ]
表示任务在2023/1/12日开始,但直到现在也没有结束,或者没有人知道他是什么时候结束的。如果你嫌写一个,
很麻烦,你也可以不写。
phase
phase表示现在当前任务处于哪个状态,我们推荐使用PDCA
生命周期进行标记,一共创建了5个默认值,分别是Plan
,Doing
,Check
,Action
以及Done
。
这5个标记符都有特殊的含义,无论你是否在前言中声明,都可以使用这5个状态,但是你可以为这5个状态创建别名,或者定义自己喜欢的状态。
catalpa 支持前言语法,将对全局有影响的内容在前言中进行标记,例如标题、所有标签、成员、进程等, 这些内容将在后面的 catalpa 中用到。
前言中的配置项可以是字符串或者列表,列表使用,
分割列表中的各项,
通常列表项支持使用alias
进行重命名,也可以使用->
代替alias
使文档更简洁,
当使用别名的时候,相当于使用了别名对应的原始名称。
如果对一个标记进行多次重命名,则会不断向上追溯,直到找不到更上一层。
例如tags: A, A alias B, B alias C
, 当你在标签中使用C的时候,就相当于使用的是A。
title
title是一个字符串,表示当前看板的标题。
tags
tags是一个列表,在当前看板中声明常用的标签,相当于为团队创建一种规范, 尽可能地使用前言中声明的标签,而不是自定义一个语义相似的其他标签。 在编写catalpa时,编辑器可以使用声明的列表提供代码提示和自动补全功能。 可以使用alias关键字声明标签的别名,在项目中使用别名,相当于使用了原始的标签。
members
members是一个列表,用来声明团队的全部成员,
All
是一个特殊的member标识,不需要声明就可以使用,用来表示members列表中的所有成员。
你可以使用alias
关键字对声明的成员进行重命名,例如小潘 alias Tim
就是为小潘
声明了一个别名Tim
,当你在member中使用Tim
时就相当于使用了小潘
。
phases
phases是一个列表,用来声明看板中任务可以处于的阶段。
我们声明了5个默认的阶段,分别是Plan
,Doing
,Check
,Action
以及Done
。
需要注意的是这5个标记有可能是别名,这主要取决于你使用的解析器是否实现这个功能。 如果这5个标记是别名,那么在解析后,状态将显示为原始的标记,相当于解析器已经声明了5个标记,然后创建了对应的5个别名。
假如解析器默认声明了:
---
phases: 🎯,🏃♂️,🔎,📦,🏆,🎯 alias Plan, 🏃♂️ alias Doing, 🔎 alias Check, 📦 alias Action, 🏆 alias Done
---
那么你在使用Plan Doing Check Action Done的时候就会解析为原始的emoji符号。
别名是可以被剥夺的,比如声明phases: A, B, A alias C, B alias C
,当你使用C的时候,解析器会将C解析为B,而不是A。
所以你也可以声明自己的标记和别名覆盖解析器缺省的标记。
---
title: 项目看板
tags: lexer, parser, viewer, editor
members: mrchar
---
计划中:
实现分组 #parser :Doing
解析别名 #viewer :Plan
代码格式化 #editor :Plan
使用lezer实现词法解析器 :Plan
实现CodeMirror的自动补全功能 :Plan
已完成:
任务描述 :Done
子任务 :Done
标记 (标签、成员、截止日、开始结束时间、阶段) :Done
前言 :Done
自定义label :Done
backlog = *(task CRLF)
[task]
task = WROD [suffix] *(CRLF sub-task)
sub-task = +SP WROD [suffix]
suffix = +label / +label +SP
label = +SP(tag / member / ddl / duration / begin / end / phase)
tag = ("tag:" / "@") *SP WROD
member = ("member:" / "#") *SP WROD
ddl = ("ddl:" / "!") *SP TIME
begin = ("begin:" / ">") *SP TIME
end = ("end:" / "<") *SP TIME
phase = ("phase:" / ":") *SP WROD
duration = "<"TIME[, *SP [TIME]] ">"
WROD = VCHAR / 2*VCHAR / VCHAR +(SP / VCHAR) VCHAR; 空格和可视字符
TIME = 4DIGIT "/" 1*2DIGIT "/" 1*2DIGIT; 时间格式
VCHAR = %x21-7E / %x80-FFFFFFFF; 可视字符
DIGIT = %x30-39; 数字(0-9)
SP = %x20; 空格
CRLF = CR LF; 回车换行
CR = %x0D; 回车
LF = %x0A; 换行