mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-02 00:52:44 +01:00
5b89670a31
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.
127 lines
5.1 KiB
Handlebars
127 lines
5.1 KiB
Handlebars
{{template "base/head" .}}
|
|
<div role="main" aria-label="{{.Title}}" class="page-content repository wiki view">
|
|
{{template "repo/header" .}}
|
|
{{$title := .title}}
|
|
<div class="ui container">
|
|
<div class="repo-button-row gt-df gt-ac gt-sb gt-fw">
|
|
<div class="gt-df gt-ac">
|
|
<div class="choose page">
|
|
<div class="ui floating filter dropdown" data-no-results="{{.locale.Tr "repo.pulls.no_results"}}">
|
|
<div class="ui basic small button">
|
|
<span class="text">
|
|
{{.locale.Tr "repo.wiki.page"}}:
|
|
<strong>{{$title}}</strong>
|
|
</span>
|
|
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
|
</div>
|
|
<div class="menu">
|
|
<div class="ui icon search input">
|
|
<i class="icon gt-df gt-ac gt-jc gt-m-0">{{svg "octicon-filter" 16}}</i>
|
|
<input name="search" placeholder="{{.locale.Tr "repo.wiki.filter_page"}}...">
|
|
</div>
|
|
<div class="scrolling menu">
|
|
{{range .Pages}}
|
|
<div class="item {{if eq $.Title .Name}}selected{{end}}" data-url="{{$.RepoLink}}/wiki/{{.SubURL}}">{{.Name}}</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="gt-df gt-ac">
|
|
<div class="ui action small input" id="clone-panel">
|
|
{{template "repo/clone_buttons" .}}
|
|
{{template "repo/clone_script" .}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="ui dividing header">
|
|
<div class="ui stackable grid">
|
|
<div class="eight wide column">
|
|
<a class="file-revisions-btn ui basic button" title="{{.locale.Tr "repo.wiki.file_revision"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}?action=_revision" ><span>{{.CommitCount}}</span> {{svg "octicon-history"}}</a>
|
|
{{$title}}
|
|
<div class="ui sub header">
|
|
{{$timeSince := TimeSince .Author.When $.locale}}
|
|
{{.locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince | Safe}}
|
|
</div>
|
|
</div>
|
|
<div class="eight wide right aligned column">
|
|
{{if .EscapeStatus.Escaped}}
|
|
<a class="ui small button unescape-button gt-hidden">{{.locale.Tr "repo.unescape_control_characters"}}</a>
|
|
<a class="ui small button escape-button">{{.locale.Tr "repo.escape_control_characters"}}</a>
|
|
{{end}}
|
|
{{if and .CanWriteWiki (not .Repository.IsMirror)}}
|
|
<div class="ui right">
|
|
<a class="ui small button" href="{{.RepoLink}}/wiki/{{.PageURL}}?action=_edit">{{.locale.Tr "repo.wiki.edit_page_button"}}</a>
|
|
<a class="ui green small button" href="{{.RepoLink}}/wiki?action=_new">{{.locale.Tr "repo.wiki.new_page_button"}}</a>
|
|
<a class="ui red small button delete-button" href="" data-url="{{.RepoLink}}/wiki/{{.PageURL}}?action=_delete" data-id="{{.PageURL}}">{{.locale.Tr "repo.wiki.delete_page_button"}}</a>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{if .FormatWarning}}
|
|
<div class="ui negative message">
|
|
<p>{{.FormatWarning}}</p>
|
|
</div>
|
|
{{end}}
|
|
<div class="ui {{if or .sidebarPresent .toc}}grid equal width{{end}}" style="margin-top: 1rem;">
|
|
<div class="ui {{if or .sidebarPresent .toc}}eleven wide column{{end}} segment markup wiki-content-main">
|
|
{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
|
|
{{.content | Safe}}
|
|
</div>
|
|
{{if or .sidebarPresent .toc}}
|
|
<div class="column" style="padding-top: 0;">
|
|
{{if .toc}}
|
|
<div class="ui segment wiki-content-toc">
|
|
<details open>
|
|
<summary>
|
|
<div class="ui header">{{.locale.Tr "toc"}}</div>
|
|
</summary>
|
|
{{$level := 0}}
|
|
{{range .toc}}
|
|
{{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}}
|
|
{{range Iterate $level}}</ul>{{end}}
|
|
</details>
|
|
</div>
|
|
{{end}}
|
|
{{if .sidebarPresent}}
|
|
<div class="ui segment wiki-content-sidebar">
|
|
{{if and .CanWriteWiki (not .Repository.IsMirror)}}
|
|
<a class="ui right floated muted" href="{{.RepoLink}}/wiki/_Sidebar?action=_edit" aria-label="{{.locale.Tr "repo.wiki.edit_page_button"}}">{{svg "octicon-pencil"}}</a>
|
|
{{end}}
|
|
{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .sidebarEscapeStatus "root" $}}
|
|
{{.sidebarContent | Safe}}
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{if .footerPresent}}
|
|
<div class="ui segment wiki-content-footer">
|
|
{{if and .CanWriteWiki (not .Repository.IsMirror)}}
|
|
<a class="ui right floated muted" href="{{.RepoLink}}/wiki/_Footer?action=_edit" aria-label="{{.locale.Tr "repo.wiki.edit_page_button"}}">{{svg "octicon-pencil"}}</a>
|
|
{{end}}
|
|
{{template "repo/unicode_escape_prompt" dict "footerEscapeStatus" .sidebarEscapeStatus "root" $}}
|
|
{{.footerContent | Safe}}
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ui small basic delete modal">
|
|
<div class="ui icon header">
|
|
{{svg "octicon-trash"}}
|
|
{{.locale.Tr "repo.wiki.delete_page_button"}}
|
|
</div>
|
|
<div class="content">
|
|
<p>{{.locale.Tr "repo.wiki.delete_page_notice_1" ($title|Escape) | Safe}}</p>
|
|
</div>
|
|
{{template "base/delete_modal_actions" .}}
|
|
</div>
|
|
|
|
{{template "base/footer" .}}
|