mirror of
https://github.com/go-gitea/gitea
synced 2025-07-22 18:28:37 +00:00
Use a general Eval function for expressions in templates. (#23927)
One of the proposals in #23328 This PR introduces a simple expression calculator (templates/eval/eval.go), it can do basic expression calculations. Many untested template helper functions like `Mul` `Add` can be replaced by this new approach. Then these `Add` / `Mul` / `percentage` / `Subtract` / `DiffStatsWidth` could all use this `Eval`. And it provides enhancements for Golang templates, and improves readability. Some examples: ---- * Before: `{{Add (Mul $glyph.Row 12) 12}}` * After: `{{Eval $glyph.Row "*" 12 "+" 12}}` ---- * Before: `{{if lt (Add $i 1) (len $.Topics)}}` * After: `{{if Eval $i "+" 1 "<" (len $.Topics)}}` ## FAQ ### Why not use an existing expression package? We need a highly customized expression engine: * do the calculation on the fly, without pre-compiling * deal with int/int64/float64 types, to make the result could be used in Golang template. * make the syntax could be used in the Golang template directly * do not introduce too much complex or strange syntax, we just need a simple calculator. * it needs to strictly follow Golang template's behavior, for example, Golang template treats all non-zero values as truth, but many 3rd packages don't do so. ### What's the benefit? * Developers don't need to add more `Add`/`Mul`/`Sub`-like functions, they were getting more and more. Now, only one `Eval` is enough for all cases. * The new code reads better than old `{{Add (Mul $glyph.Row 12) 12}}`, the old one isn't familiar to most procedural programming developers (eg, the Golang expression syntax). * The `Eval` is fully covered by tests, many old `Add`/`Mul`-like functions were never tested. ### The performance? It doesn't use `reflect`, it doesn't need to parse or compile when used in Golang template, the performance is as fast as native Go template. ### Is it too complex? Could it be unstable? The expression calculator program is a common homework for computer science students, and it's widely used as a teaching and practicing purpose for developers. The algorithm is pretty well-known. The behavior can be clearly defined, it is stable.
This commit is contained in:
@@ -69,11 +69,12 @@
|
||||
<div class="commit-divergence">
|
||||
<div class="bar-group">
|
||||
<div class="count count-behind">{{.CommitsBehind}}</div>
|
||||
<div class="bar bar-behind" style="width: {{percentage .CommitsBehind .CommitsBehind .CommitsAhead}}%"></div>
|
||||
{{/* old code bears 0/0.0 = NaN output, so it might output invalid "width: NaNpx", it just works and doesn't caues any problem. */}}
|
||||
<div class="bar bar-behind" style="width: {{Eval 100 "*" .CommitsBehind "/" "(" .CommitsBehind "+" .CommitsAhead "+" 0.0 ")"}}%"></div>
|
||||
</div>
|
||||
<div class="bar-group">
|
||||
<div class="count count-ahead">{{.CommitsAhead}}</div>
|
||||
<div class="bar bar-ahead" style="width: {{percentage .CommitsAhead .CommitsBehind .CommitsAhead}}%"></div>
|
||||
<div class="bar bar-ahead" style="width: {{Eval 100 "*" .CommitsAhead "/" "(" .CommitsBehind "+" .CommitsAhead "+" 0.0 ")"}}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="timeline-item commits-list">
|
||||
{{range .comment.Commits}}
|
||||
{{$tag := printf "%s-%d" $.comment.HashTag $index}}
|
||||
{{$index = Add $index 1}}
|
||||
{{$index = Eval $index "+" 1}}
|
||||
<div class="singular-commit" id="{{$tag}}">
|
||||
<span class="badge badge-commit">{{svg "octicon-git-commit"}}</span>
|
||||
{{if .User}}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{{Add .file.Addition .file.Deletion}}
|
||||
<span class="diff-stats-bar gt-mx-3" data-tooltip-content="{{.root.locale.Tr "repo.diff.stats_desc_file" (Add .file.Addition .file.Deletion) .file.Addition .file.Deletion | Str2html}}">
|
||||
{{Eval .file.Addition "+" .file.Deletion}}
|
||||
<span class="diff-stats-bar gt-mx-3" data-tooltip-content="{{.root.locale.Tr "repo.diff.stats_desc_file" (Eval .file.Addition "+" .file.Deletion) .file.Addition .file.Deletion | Str2html}}">
|
||||
<div class="diff-stats-add-bar" style="width: {{DiffStatsWidth .file.Addition .file.Deletion}}%"></div>
|
||||
</span>
|
||||
|
@@ -12,7 +12,7 @@
|
||||
<div class="ui breadcrumb field {{if .Err_TreePath}}error{{end}}">
|
||||
<a class="section" href="{{$.BranchLink}}">{{.Repository.Name}}</a>
|
||||
{{$n := len .TreeNames}}
|
||||
{{$l := Subtract $n 1}}
|
||||
{{$l := Eval $n "-" 1}}
|
||||
{{range $i, $v := .TreeNames}}
|
||||
<div class="divider"> / </div>
|
||||
{{if eq $i $l}}
|
||||
|
@@ -10,7 +10,7 @@
|
||||
<div class="ui breadcrumb field {{if .Err_TreePath}}error{{end}}">
|
||||
<a class="section" href="{{$.BranchLink}}">{{.Repository.Name}}</a>
|
||||
{{$n := len .TreeNames}}
|
||||
{{$l := Subtract $n 1}}
|
||||
{{$l := Eval $n "-" 1}}
|
||||
{{range $i, $v := .TreeNames}}
|
||||
<div class="divider"> / </div>
|
||||
{{if eq $i $l}}
|
||||
|
@@ -1,22 +1,22 @@
|
||||
<div id="rel-container">
|
||||
<svg viewbox="{{Mul .Graph.MinColumn 5}} {{Mul .Graph.MinRow 12}} {{Add (Mul .Graph.Width 5) 5}} {{Mul .Graph.Height 12}}" width="{{Add (Mul .Graph.Width 10) 10}}px">
|
||||
<svg viewbox="{{Eval .Graph.MinColumn "*" 5}} {{Eval .Graph.MinRow "*" 12}} {{Eval .Graph.Width "*" 5 "+" 5}} {{Eval .Graph.Height "*" 12}}" width="{{Eval .Graph.Width "*" 10 "+" 10}}px">
|
||||
{{range $flowid, $flow := .Graph.Flows}}
|
||||
<g id="flow-{{$flow.ID}}" class="flow-group flow-color-{{$flow.ColorNumber}} flow-color-16-{{$flow.Color16}}" data-flow="{{$flow.ID}}" data-color="{{$flow.ColorNumber}}">
|
||||
<path d="{{range $i, $glyph := $flow.Glyphs -}}
|
||||
{{- if or (eq $glyph.Glyph '*') (eq $glyph.Glyph '|') -}}
|
||||
M {{Add (Mul $glyph.Column 5) 5}} {{Add (Mul $glyph.Row 12) 0}} v 12 {{/* */ -}}
|
||||
M {{Eval $glyph.Column "*" 5 "+" 5}} {{Eval $glyph.Row "*" 12 "+" 0}} v 12 {{/* */ -}}
|
||||
{{- else if eq $glyph.Glyph '/' -}}
|
||||
M {{Add (Mul $glyph.Column 5) 10}} {{Add (Mul $glyph.Row 12) 0}} l -10 12 {{/* */ -}}
|
||||
M {{Eval $glyph.Column "*" 5 "+" 10}} {{Eval $glyph.Row "*" 12 "+" 0}} l -10 12 {{/* */ -}}
|
||||
{{- else if eq $glyph.Glyph '\\' -}}
|
||||
M {{Add (Mul $glyph.Column 5) 0}} {{Add (Mul $glyph.Row 12) 0}} l 10 12 {{/* */ -}}
|
||||
M {{Eval $glyph.Column "*" 5 "+" 0}} {{Eval $glyph.Row "*" 12 "+" 0}} l 10 12 {{/* */ -}}
|
||||
{{- else if or (eq $glyph.Glyph '-') (eq $glyph.Glyph '.') -}}
|
||||
M {{Add (Mul $glyph.Column 5) 0}} {{Add (Mul $glyph.Row 12) 12}} h 5 {{/* */ -}}
|
||||
M {{Eval $glyph.Column "*" 5 "+" 0}} {{Eval $glyph.Row "*" 12 "+" 12}} h 5 {{/* */ -}}
|
||||
{{- else if eq $glyph.Glyph '_' -}}
|
||||
M {{Add (Mul $glyph.Column 5) 0}} {{Add (Mul $glyph.Row 12) 12}} h 10 {{/* */ -}}
|
||||
M {{Eval $glyph.Column "*" 5 "+" 0}} {{Eval $glyph.Row "*" 12 "+" 12}} h 10 {{/* */ -}}
|
||||
{{- end -}}
|
||||
{{- end}}" stroke-width="1" fill="none" id="flow-{{$flow.ID}}-path" stroke-linecap="round"></path>
|
||||
{{range $flow.Commits}}
|
||||
<circle class="flow-commit" cx="{{Add (Mul .Column 5) 5}}" cy="{{Add (Mul .Row 12) 6}}" r="2.5" stroke="none" id="flow-commit-{{.Rev}}" data-rev="{{.Rev}}"></circle>
|
||||
<circle class="flow-commit" cx="{{Eval .Column "*" 5 "+" 5}}" cy="{{Eval .Row "*" 12 "+" 6}}" r="2.5" stroke="none" id="flow-commit-{{.Rev}}" data-rev="{{.Rev}}"></circle>
|
||||
{{end}}
|
||||
</g>
|
||||
{{end}}
|
||||
|
@@ -37,7 +37,7 @@
|
||||
<div class="ui form gt-hidden gt-df gt-mt-4" id="topic_edit">
|
||||
<div class="field gt-f1 gt-mr-3">
|
||||
<div class="ui fluid multiple search selection dropdown" data-text-count-prompt="{{.locale.Tr "repo.topic.count_prompt"}}" data-text-format-prompt="{{.locale.Tr "repo.topic.format_prompt"}}">
|
||||
<input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if lt (Add $i 1) (len $.Topics)}},{{end}}{{end}}">
|
||||
<input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if Eval $i "+" 1 "<" (len $.Topics)}},{{end}}{{end}}">
|
||||
{{range .Topics}}
|
||||
{{/* keey the same layout as Fomantic UI generated labels */}}
|
||||
<a class="ui label transition visible gt-cursor-default" data-value="{{.Name}}" style="display: inline-block !important;">{{.Name}}{{svg "octicon-x" 16 "delete icon"}}</a>
|
||||
@@ -61,7 +61,7 @@
|
||||
<div class="gt-df gt-ac gt-fw gt-gap-y-3">
|
||||
{{template "repo/branch_dropdown" dict "root" .}}
|
||||
{{$n := len .TreeNames}}
|
||||
{{$l := Subtract $n 1}}
|
||||
{{$l := Eval $n "-" 1}}
|
||||
<!-- If home page, show new pr. If not, show breadcrumb -->
|
||||
{{if eq $n 0}}
|
||||
{{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
|
||||
|
@@ -100,7 +100,7 @@
|
||||
<table>
|
||||
<tbody>
|
||||
{{range $idx, $code := .FileContent}}
|
||||
{{$line := Add $idx 1}}
|
||||
{{$line := Eval $idx "+" 1}}
|
||||
<tr>
|
||||
<td id="L{{$line}}" class="lines-num"><span id="L{{$line}}" data-line-number="{{$line}}"></span></td>
|
||||
{{if $.EscapeStatus.Escaped}}
|
||||
|
@@ -80,8 +80,8 @@
|
||||
</summary>
|
||||
{{$level := 0}}
|
||||
{{range .toc}}
|
||||
{{if lt $level .Level}}{{range Iterate (Subtract .Level $level)}}<ul>{{end}}{{end}}
|
||||
{{if gt $level .Level}}{{range Iterate (Subtract $level .Level)}}</ul>{{end}}{{end}}
|
||||
{{if lt $level .Level}}{{range Iterate (Eval .Level "-" $level)}}<ul>{{end}}{{end}}
|
||||
{{if gt $level .Level}}{{range Iterate (Eval $level "-" .Level)}}</ul>{{end}}{{end}}
|
||||
{{$level = .Level}}
|
||||
<li><a href="#{{.ID}}">{{.Text}}</a></li>
|
||||
{{end}}
|
||||
|
Reference in New Issue
Block a user