summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorJonas Smedegaard <dr@jones.dk>2024-04-01 03:22:12 +0200
committerJonas Smedegaard <dr@jones.dk>2024-04-01 03:22:12 +0200
commit0fd0eaab3b8bfc154b83a3507c3caa9d9ab556c6 (patch)
treed96bea9deadaf86f49d2bf31a30983d042c77aeb /bin
parent29cee1399c977def72f1c8e1e13d83a841b75c89 (diff)
use PlantUML for Gantt diagrams
Diffstat (limited to 'bin')
-rwxr-xr-xbin/hedgedoc2quarto200
1 files changed, 197 insertions, 3 deletions
diff --git a/bin/hedgedoc2quarto b/bin/hedgedoc2quarto
index c0bde3b..c81ddd5 100755
--- a/bin/hedgedoc2quarto
+++ b/bin/hedgedoc2quarto
@@ -26,7 +26,8 @@ and adapts embedded diagram code.
Both HedgeDoc and Quarto uses Markdown,
but different flavors,
-and they handle different subsets of Mermaid diagrams.
+and whereas both handle (different subsets of) Mermaid diagrams,
+Quarto also (through plugins) handles PlantUML diagrams.
=cut
@@ -43,8 +44,12 @@ $content =~ s/^
(?'code'.*?\n)
\k'fence'
$/
- "{mermaid}\n\%\%| fig-width: 100\%\n"
- . &mmd2mmd( $+{type}, $+{code} )
+
+# FIXME: implement option to choose output diagram language
+# "{mermaid}\n\%\%| fig-width: 100\%\n"
+# . &mmd2mmd( $+{type}, $+{code} )
+ "{.plantuml}\n\%\%| fig-width: 100\%\n"
+ . &mmd2puml( $+{type}, $+{code} )
. $+{fence}
/gsmex;
@@ -65,6 +70,195 @@ sub mmd2mmd ( $type, $code )
return "$type\n$code";
}
+sub mmd2puml ( $type, $code )
+{
+ my @newcode;
+
+ # strip special comment marker '%%QUARTO%%'
+ $code =~ s/^\s*+\K%%QUARTO%%//gm;
+
+ open my $fh, '<', \$code or die $!;
+
+ while (<$fh>) {
+
+ /^\s*+$/
+ and push @newcode, ''
+ and next;
+
+ /^(\s*+)%%PLANTUML%%\K.*/
+ and push @newcode, "$1$&"
+ and next;
+
+ # convert comments markers
+ /^(\s*+)(?:[%]{2,}(?'comment'\s*+))?+\K.*/;
+ my $indent = defined( $+{comment} ) ? "$1'$2" : $1;
+ $_ = $&;
+
+ /^title\s/i
+ and push @newcode, "${indent}$_"
+ and next;
+
+ /^excludes\s+weekends\b/i
+ and push @newcode, "${indent}saturday are closed"
+ and push @newcode, "${indent}sunday are closed"
+ and next;
+ /^weekday\s+\K(?:mon|tues|wednes|thurs|fri|satur|sun)day\b/i
+ and push @newcode, "${indent}weeks start on $&"
+ and next;
+ /^(?:date|axis)Format\s/i
+ and push @newcode, "${indent}'UNSUPPORTED: $_"
+ and next;
+ /^todayMarker\s+(off|on)\b/i
+ and push @newcode, "${indent}'UNSUPPORTED' $_"
+ and next;
+ /^section\s+\K\S+(?:\s+\S+)*/i
+ and push @newcode, "${indent}-- $& --"
+ and next;
+
+ if (/^tickInterval\s+(?'tickAmount'\d+)(?'tickUnit'millisecond|second|minute|hour|day|week|month)\s*$/i
+ )
+ {
+ push @newcode, "${indent}projectscale daily"
+ and next
+ if $+{tickAmount} eq 1
+ and $+{tickUnit} eq 'day';
+ push @newcode, "${indent}projectscale weekly" and next
+ if $+{tickAmount} eq 1 and $+{tickUnit} eq 'week'
+ or $+{tickAmount} eq 7 and $+{tickUnit} eq 'day';
+ push @newcode, "${indent}projectscale monthly"
+ and next
+ if $+{tickAmount} eq 1
+ and $+{tickUnit} eq 'month';
+ push @newcode, "${indent}projectscale quarterly"
+ and next
+ if $+{tickAmount} eq 3
+ and $+{tickUnit} eq 'month';
+ push @newcode, "${indent}projectscale yearly"
+ and next
+ if $+{tickAmount} eq 12
+ and $+{tickUnit} eq 'month';
+ push @newcode, "${indent}'UNSUPPORTED' $&"
+ and next;
+ }
+
+ /^
+ (?'title'[^:\n]+)
+ \s*+:\s*+
+
+ # optional tags
+ (?:
+ (?:
+ (?'active'active)
+ |
+ (?'done'done)
+ |
+ (?'crit'crit)
+ |
+ (?'milestone'milestone)
+ )\s*+
+ ,\s*+
+ )?+
+
+ (?:
+ # optional tertiary item
+ (?:
+ (?'taskID'(?&id))\s*+
+ ,\s*+
+ (?=.*,) # several items must follow
+ )?+
+
+ # optional secondary item
+ (?:
+ (?'startDate'(?&date))
+ |
+ after
+ (?'afterTaskIDs'
+ (?:\s+(?&id))++
+ )
+ )\s*+
+ ,\s*+
+ )?+
+
+ # required main item
+ (?:
+ (?'endDate'(?&date))
+ |
+ until
+ (?'untilTaskIDs'
+ (?:\s+(?&id))++
+ )
+ |
+ (?'duration'\d+)
+ \s*+d
+ )\s*+
+ (?(DEFINE)
+ (?'id'[^\s\d,][^\s,]*+) # assume digit as lead caracter is illegal
+ (?'date'\d\d\d\d(?:-\d\d(?:-\d\d)?+)?+)
+ )
+ $/x
+ or defined( $+{comment} )
+ and push @newcode, "${indent}$_"
+ and next
+ or die "unhandled syntax on line $.: $_";
+
+ defined( $+{active} )
+ or defined( $+{done} )
+ or defined( $+{crit} )
+ and die "unhandled tag on line $.: $_";
+
+ my $task = "${indent}\[$+{title}]";
+ my $taskref = $task;
+
+ # optional 3rd item
+ if ( $+{taskID} ) {
+ $task .= " as [$+{taskID}]";
+ $taskref = "${indent}\[$+{taskID}]";
+ }
+
+ if ( defined( $+{afterTaskIDs} ) ) {
+ my @reqs = split ' ', $+{afterTaskIDs};
+
+ if ( $+{milestone} ) {
+ push @newcode, "$task happens at [$_]'s end" for @reqs;
+ }
+ elsif ( $+{endDate} ) {
+ push @newcode, "$task ends $+{endDate}";
+ push( @newcode, "$taskref starts at [$_]'s end" ) for @reqs;
+ }
+ elsif ( defined( $+{untilTaskIDs} ) ) {
+ my @reqsEnd = split ' ', $+{untilTaskIDs};
+ push @newcode, "$task ends at [$_]'s end" for @reqsEnd;
+ push( @newcode, "$taskref starts at [$_]'s end" ) for @reqs;
+ }
+ else {
+ push @newcode, "$task requires $+{duration} days";
+ push( @newcode, "$taskref starts at [$_]'s end" ) for @reqs;
+ }
+ }
+ else {
+ if ( $+{milestone} ) {
+ push @newcode, "$task happens $+{startDate}";
+ }
+ elsif ( $+{endDate} ) {
+ push @newcode,
+ "$task starts $+{startDate} and ends $+{ednDate}";
+ }
+ elsif ( defined( $+{untilTaskIDs} ) ) {
+ my @reqsEnd = split ' ', $+{untilTaskIDs};
+ push @newcode, "$task starts $+{startDate}";
+ push @newcode, "$task ends at [$_]'s end" for @reqsEnd;
+ }
+ else {
+ push @newcode,
+ "$task starts $+{startDate} and requires $+{duration} days";
+ }
+ }
+ }
+
+ $" = "\n";
+ return "\@start$type\n@newcode\n\@end$type\n";
+}
+
=encoding UTF-8
=head1 AUTHOR